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

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Dec 1 18:52:34 UTC 2016


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

ghisvail-guest pushed a commit to branch debian/master
in repository dcm2niix.

commit 82ca9e9cfdbc9b961028db334abc5c53304c4ccc
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date:   Thu Dec 1 16:46:54 2016 +0000

    New upstream version 1.0.20161101
---
 .gitignore                                         |    1 +
 .travis.yml                                        |   39 +
 CMakeLists.txt                                     |   37 +
 README.md                                          |  261 ++
 appveyor.yml                                       |   50 +
 batch_config.yml                                   |   16 +
 console/CMakeLists.txt                             |   93 +
 console/jpg_0XC3.cpp                               |  494 ++
 console/jpg_0XC3.h                                 |   23 +
 console/main_console.cpp                           |  211 +
 console/main_console_batch.cpp                     |   80 +
 console/makefile                                   |    9 +
 console/miniz.c                                    | 4919 ++++++++++++++++++++
 console/nifti1.h                                   | 1491 ++++++
 console/nifti1_io_core.cpp                         |  479 ++
 console/nifti1_io_core.h                           |   74 +
 console/nii_dicom.cpp                              | 3273 +++++++++++++
 console/nii_dicom.h                                |   84 +
 console/nii_dicom_batch.cpp                        | 2347 ++++++++++
 console/nii_dicom_batch.h                          |   34 +
 console/nii_ortho.cpp                              |  378 ++
 console/nii_ortho.h                                |   16 +
 console/nii_ostu_ml.cpp                            |  628 +++
 console/nii_ostu_ml.h                              |   19 +
 console/tinydir.h                                  |  441 ++
 console/ujpeg.cpp                                  |  668 +++
 console/ujpeg.h                                    |   69 +
 console/unused/untgz.c                             |  929 ++++
 console/unused/untgz.h                             |    8 +
 cpfiles.command                                    |   18 +
 dcm2laz/LibTar.pas                                 |  967 ++++
 dcm2laz/dcm2.ico                                   |  Bin 0 -> 17542 bytes
 dcm2laz/dcm2.lpi                                   |   93 +
 dcm2laz/dcm2.lpr                                   |   21 +
 dcm2laz/dcm2.lps                                   |  195 +
 dcm2laz/dcm2.res                                   |  Bin 0 -> 18568 bytes
 dcm2laz/dcm2niigui.ico                             |  Bin 0 -> 17542 bytes
 dcm2laz/form.lfm                                   |  134 +
 dcm2laz/form.pas                                   |  367 ++
 dcm2laz/untar.pas                                  |  168 +
 license.txt                                        |   36 +
 qtGui/Q_DebugStream.h                              |   71 +
 qtGui/Q_DebugStream_new.h                          |   97 +
 qtGui/dcm2.pro                                     |   44 +
 qtGui/dcm2.pro.user                                |  263 ++
 qtGui/main.cpp                                     |   12 +
 qtGui/mainwindow.cpp                               |  121 +
 qtGui/mainwindow.h                                 |   60 +
 qtGui/mainwindow.ui                                |  205 +
 qtGui/nifti1_io_core.c                             |  472 ++
 qtGui/nii_dicom.c                                  | 1852 ++++++++
 qtGui/nii_dicom_batch.c                            | 1475 ++++++
 qtGui/nii_ortho.c                                  |  374 ++
 qtGui/notes.txt                                    |    1 +
 rmfiles.command                                    |   16 +
 ucm.cmake                                          |  611 +++
 wxWidgets/clipboard.cpp                            |  282 ++
 wxWidgets/nifti1_io_core.c                         |  472 ++
 wxWidgets/nii_dicom.c                              | 1852 ++++++++
 wxWidgets/nii_dicom_batch.c                        | 1475 ++++++
 wxWidgets/nii_ortho.c                              |  374 ++
 wxWidgets/readme.txt                               |   24 +
 xcode/core                                         |  441 ++
 xcode/dcm2.xcodeproj/project.pbxproj               |  390 ++
 .../project.xcworkspace/contents.xcworkspacedata   |    7 +
 .../xcshareddata/dcm2.xccheckout                   |   41 +
 .../UserInterfaceState.xcuserstate                 |  Bin 0 -> 45523 bytes
 .../UserInterfaceState.xcuserstate                 |  Bin 0 -> 110212 bytes
 .../WorkspaceSettings.xcsettings                   |   10 +
 .../xcschemes/dcm2.xcscheme                        |   94 +
 .../xcschemes/xcschememanagement.plist             |   27 +
 .../xcdebugger/Breakpoints.xcbkptlist              |    5 +
 .../rorden.xcuserdatad/xcschemes/dcm2.xcscheme     |   96 +
 .../xcschemes/xcschememanagement.plist             |   27 +
 xcode/dcm2/AppDelegate.h                           |   41 +
 xcode/dcm2/AppDelegate.m                           |  201 +
 xcode/dcm2/dcm2-Info.plist                         |   47 +
 xcode/dcm2/dcm2-Prefix.pch                         |    7 +
 xcode/dcm2/en.lproj/Credits.rtf                    |   23 +
 xcode/dcm2/en.lproj/InfoPlist.strings              |    2 +
 xcode/dcm2/en.lproj/MainMenu.xib                   | 1242 +++++
 xcode/dcm2/main.m                                  |   14 +
 xcode/dcm2/myWindow.h                              |   13 +
 xcode/dcm2/myWindow.m                              |   46 +
 xcode/dcm2niigui.icns                              |  Bin 0 -> 41994 bytes
 85 files changed, 32097 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2f84669
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,39 @@
+language: cpp
+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
+    - os: osx
+      env: TARGET=osx
+
+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 -
+
+before_deploy:
+  - zip ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.zip build/bin/dcm2niibatch build/bin/dcm2niix
+
+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 [...]
+  file_glob: true
+  file: ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.*
+  # don't delete the artifacts from previous phases
+  skip_cleanup: true
+  # deploy when a new tag is pushed
+  on:
+    tags: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..707d390
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,37 @@
+project(dcm2niix)
+cmake_minimum_required(VERSION 2.6)
+
+include(ucm.cmake)
+
+# 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)
+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)
+
+##
+## Sub-projects
+##
+add_subdirectory(console)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9d6b339
--- /dev/null
+++ b/README.md
@@ -0,0 +1,261 @@
+[![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.
+
+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).
+
+12-Dec-2015
+ - Support PAR/REC FP values when possible (see PMC3998685).
+
+11-Nov-2015
+ - Minor refinements.
+
+12-June-2015
+ - Uses less memory (helpful for large datasets).
+
+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
+```
+
+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
+```
+`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
+
+e.g. the dependencies can be installed as follows:
+
+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
+```
+
+
+### 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
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..f964fdc
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,50 @@
+version: build-{build}
+
+branches:
+  only:
+  - master
+
+configuration: Release
+
+platform: x64
+
+clone_depth: 1
+
+clone_folder: c:\projects\dcm2niix
+
+init:
+- ps: >-
+    $version = Get-Date -Format "d-MMM-yyyy"
+
+    $githash = $env:APPVEYOR_REPO_COMMIT.Substring(0, 7)
+
+    Update-AppveyorBuild -Version "$version-$githash"
+
+before_build:
+- cmd: >-
+    echo "Clone yaml-cpp"
+
+    git clone -q --depth=1 --branch=master https://github.com/jbeder/yaml-cpp.git c:\projects\dcm2niix\yaml-cpp
+
+    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 ..\
+
+build:
+  project: c:\projects\dcm2niix\build\dcm2niix.sln
+
+  verbosity: normal
+
+after_build:
+- ps: >-
+    cd c:\projects\dcm2niix
+
+    7z a dcm2niix-$version-win.zip c:\projects\dcm2niix\bin\Release\* >$null
+
+artifacts:
+  - path: 'dcm2niix-*.zip'
+    name: dcm2niix
diff --git a/batch_config.yml b/batch_config.yml
new file mode 100644
index 0000000..c6b892a
--- /dev/null
+++ b/batch_config.yml
@@ -0,0 +1,16 @@
+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
\ No newline at end of file
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
new file mode 100644
index 0000000..30199f8
--- /dev/null
+++ b/console/CMakeLists.txt
@@ -0,0 +1,93 @@
+project(console)
+set(PROGRAMS dcm2niix)
+
+if(POLICY CMP0054)
+  cmake_policy(SET CMP0054 NEW)
+endif()
+
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+    # using Clang
+    add_definitions(-dead_strip)
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+    # using GCC
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
+    # using Intel C++
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+    # using Visual Studio C++
+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)
+
+add_executable(dcm2niix
+        main_console.cpp
+        nii_dicom.cpp
+        jpg_0XC3.cpp
+        ujpeg.cpp
+        nifti1_io_core.cpp
+        nii_ortho.cpp
+        nii_dicom_batch.cpp)
+
+
+if (BATCH_VERSION)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+    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 ()
+
+    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})
+    else()
+        target_link_libraries(dcm2niibatch yaml-cpp)
+    endif()
+
+
+    if (YAML_CPP_INCLUDE_DIR)
+        include_directories(${YAML_CPP_INCLUDE_DIR})
+    endif ()
+endif ()
+
+install(TARGETS ${PROGRAMS} DESTINATION bin)
diff --git a/console/jpg_0XC3.cpp b/console/jpg_0XC3.cpp
new file mode 100644
index 0000000..742ea1c
--- /dev/null
+++ b/console/jpg_0XC3.cpp
@@ -0,0 +1,494 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "jpg_0XC3.h"
+
+unsigned char  readByte(unsigned char *lRawRA, long *lRawPos, long lRawSz) {
+    unsigned char ret = 0x00;
+    if (*lRawPos < lRawSz)
+        ret = lRawRA[*lRawPos];
+    (*lRawPos)++;
+    return ret;
+}// readByte()
+
+uint16_t  readWord(unsigned char *lRawRA, long *lRawPos, long lRawSz) {
+    return ( (readByte(lRawRA, lRawPos, lRawSz) << 8) + readByte(lRawRA, lRawPos, lRawSz));
+}// readWord()
+
+int readBit(unsigned char *lRawRA, long *lRawPos,  int *lCurrentBitPos) {//Read the next single bit
+    int result = (lRawRA[*lRawPos] >> (7 - *lCurrentBitPos)) & 1;
+    (*lCurrentBitPos)++;
+    if (*lCurrentBitPos == 8) {
+        (*lRawPos)++;
+        *lCurrentBitPos = 0;
+    }
+    return result;
+}// readBit()
+
+int bitMask(int bits) {
+    return ( (2 << (bits - 1)) -1);
+}// bitMask()
+
+int readBits (unsigned char *lRawRA, long *lRawPos,  int *lCurrentBitPos, int  lNum) { //lNum: bits to read, not to exceed 16
+    int result = lRawRA[*lRawPos];
+    result = (result << 8) + lRawRA[(*lRawPos)+1];
+    result = (result << 8) + lRawRA[(*lRawPos)+2];
+    result = (result >> (24 - * lCurrentBitPos -lNum)) & bitMask(lNum); //lCurrentBitPos is incremented from 1, so -1
+    *lCurrentBitPos = *lCurrentBitPos + lNum;
+    if (*lCurrentBitPos > 7) {
+            *lRawPos = *lRawPos + (*lCurrentBitPos >> 3); // div 8
+            *lCurrentBitPos = *lCurrentBitPos & 7; //mod 8
+    }
+    return result;
+}// readBits()
+
+struct HufTables {
+    uint8_t SSSSszRA[18];
+    uint8_t LookUpRA[256];
+    int DHTliRA[32];
+    int DHTstartRA[32];
+    int HufSz[32];
+    int HufCode[32];
+    int HufVal[32];
+    int MaxHufSi;
+    int MaxHufVal;
+};// HufTables()
+
+int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBitPos, struct HufTables l) {
+    int lByte = (lRawRA[*lRawPos] << *lCurrentBitPos) + (lRawRA[*lRawPos+1] >> (8- *lCurrentBitPos));
+    lByte = lByte & 255;
+    int lHufValSSSS = l.LookUpRA[lByte];
+    if (lHufValSSSS < 255) {
+        *lCurrentBitPos = l.SSSSszRA[lHufValSSSS] + *lCurrentBitPos;
+        *lRawPos = *lRawPos + (*lCurrentBitPos >> 3);
+        *lCurrentBitPos = *lCurrentBitPos & 7;
+    } else { //full SSSS is not in the first 8-bits
+        int lInput = lByte;
+        int lInputBits = 8;
+        (*lRawPos)++; // forward 8 bits = precisely 1 byte
+        do {
+            lInputBits++;
+            lInput = (lInput << 1) + readBit(lRawRA, lRawPos, lCurrentBitPos);
+            if (l.DHTliRA[lInputBits] != 0) { //if any entires with this length
+                for (int lI = l.DHTstartRA[lInputBits]; lI <= (l.DHTstartRA[lInputBits]+l.DHTliRA[lInputBits]-1); lI++) {
+                    if (lInput == l.HufCode[lI])
+                        lHufValSSSS = l.HufVal[lI];
+                } //check each code
+            } //if any entries with this length
+            if ((lInputBits >= l.MaxHufSi) && (lHufValSSSS > 254)) {//exhausted options CR: added rev13
+                lHufValSSSS = l.MaxHufVal;
+            }
+        } while (!(lHufValSSSS < 255)); // found;
+    } //answer in first 8 bits
+    //The HufVal is referred to as the SSSS in the Codec, so it is called 'lHufValSSSS'
+    if (lHufValSSSS == 0) //NO CHANGE
+      return 0;
+    if (lHufValSSSS == 1) {
+        if (readBit(lRawRA, lRawPos, lCurrentBitPos) == 0)
+            return -1;
+        else
+            return 1;
+     }
+    if (lHufValSSSS == 16) { //ALL CHANGE 16 bit difference: Codec H.1.2.2 "No extra bits are appended after SSSS = 16 is encoded." Osiris fails here
+        return 32768;
+    }
+    //to get here - there is a 2..15 bit difference
+    int lDiff = readBits(lRawRA, lRawPos, lCurrentBitPos, lHufValSSSS);
+    if (lDiff <= bitMask(lHufValSSSS-1))  //add
+        lDiff = lDiff - bitMask(lHufValSSSS);
+    return lDiff;
+}// decodePixelDifference()
+
+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;
+    unsigned char *lImgRA8 = NULL;
+    FILE *reader = fopen(fn, "rb");
+    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);
+        return NULL; //read failure
+    }
+    fseek(reader, skipBytes, SEEK_SET);
+    unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
+    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 (verbose)
+        printf("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
+    unsigned char btS1, btS2, SOSss, SOSse, SOSahal, SOSpttrans, btMarkerType, SOSns = 0x00; //tag
+    uint8_t SOFnf, SOFprecision;
+    uint16_t SOFydim, SOFxdim; //, lRestartSegmentSz;
+    // long SOSarrayPos; //SOFarrayPos
+    int lnHufTables = 0;
+    const int kmaxFrames = 4;
+    struct HufTables l[kmaxFrames+1];
+    do { //read each marker in the header
+        do {
+            btS1 = readByte(lRawRA, &lRawPos, lRawSz);
+            if (btS1 != 0xFF) {
+                printf("JPEG header tag must begin with 0xFF\n");
+                abortGoto(); //goto abortGoto;
+            }
+            btMarkerType =  readByte(lRawRA, &lRawPos, lRawSz);
+            if ((btMarkerType == 0x01) || (btMarkerType == 0xFF) || ((btMarkerType >= 0xD0) && (btMarkerType <= 0xD7) ) )
+                btMarkerType = 0;//only process segments with length fields
+
+        } 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 (verbose)
+            printf("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);
+            SOFydim = readWord(lRawRA, &lRawPos, lRawSz);
+            SOFxdim = readWord(lRawRA, &lRawPos, lRawSz);
+            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 (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;
+            }
+            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;
+            }
+        } else if (btMarkerType == 0xC4) {//if SOF marker else if define-Huffman-tables marker (DHT)
+            if (verbose) printf(" [Huffman Length %d]\n", lSegmentLength);
+            int lFrameCount = 1;
+            do {
+                uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz ); //we read but ignore DHTtcth.
+                #pragma unused(DHTnLi) //we need to increment the input file position, but we do not care what the value is
+                DHTnLi = 0;
+                for (int lInc = 1; lInc <= 16; lInc++) {
+                    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 (DHTnLi > 17) {
+                    printf("Huffman table corrupted.\n");
+                    abortGoto(); //goto abortGoto;
+                }
+                int lIncY = 0; //frequency
+                for (int lInc = 0; lInc <= 31; lInc++) {//lInc := 0 to 31 do begin
+                    l[lFrameCount].HufVal[lInc] = -1;
+                    l[lFrameCount].HufSz[lInc] = -1;
+                    l[lFrameCount].HufCode[lInc] = -1;
+                }
+                for (int lInc = 1; lInc <= 16; lInc++) {//set the huffman size values
+                    if (l[lFrameCount].DHTliRA[lInc] > 0) {
+                        l[lFrameCount].DHTstartRA[lInc] = lIncY+1;
+                        for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lInc]; lIncX++) {
+                            lIncY++;
+                            btS1 = readByte(lRawRA, &lRawPos, lRawSz);
+                            l[lFrameCount].HufVal[lIncY] = btS1;
+                            l[lFrameCount].MaxHufVal = btS1;
+                            if ((btS1 >= 0) && (btS1 <= 16))
+                                l[lFrameCount].HufSz[lIncY] = lInc;
+                            else {
+                                printf("Huffman size array corrupted.\n");
+                                abortGoto(); //goto abortGoto;
+                            }
+                        }
+                    }
+                } //set huffman size values
+                int K = 1;
+                int Code = 0;
+                int Si = l[lFrameCount].HufSz[K];
+                do {
+                    while (Si == l[lFrameCount].HufSz[K]) {
+                        l[lFrameCount].HufCode[K] = Code;
+                        Code = Code + 1;
+                        K++;
+                    }
+                    if (K <= DHTnLi) {
+                        while (l[lFrameCount].HufSz[K] > Si) {
+                            Code = Code << 1; //Shl!!!
+                            Si = Si + 1;
+                        }//while Si
+                    }//K <= 17
+
+                } 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]);
+                lFrameCount++;
+            } while ((lSegmentEnd-lRawPos) >= 18);
+            lnHufTables = lFrameCount - 1;
+            lRawPos = (lSegmentEnd);
+            if (verbose) printf(" [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;
+            //lRestartSegmentSz = ReadWord(lRawRA, &lRawPos, lRawSz);
+            //lRawPos = lSegmentEnd;
+        } else if (btMarkerType == 0xDA) {  //if DRI marker else if read Start of Scan (SOS) marker
+            SOSns = readByte(lRawRA, &lRawPos, lRawSz);
+            //if Ns = 1 then NOT interleaved, else interleaved: see B.2.3
+            // SOSarrayPos = lRawPos; //not required...
+            if (SOSns > 0) {
+                for (int lInc = 1; lInc <= SOSns; lInc++) {
+                    btS1 = readByte(lRawRA, &lRawPos, lRawSz); //component identifier 1=Y,2=Cb,3=Cr,4=I,5=Q
+                    #pragma unused(btS1) //dummy value used to increment file position
+                    btS2 = readByte(lRawRA, &lRawPos, lRawSz); //horizontal and vertical sampling factors
+                    #pragma unused(btS2) //dummy value used to increment file position
+                }
+            }
+            SOSss = readByte(lRawRA, &lRawPos, lRawSz); //predictor selection B.3
+            SOSse = readByte(lRawRA, &lRawPos, lRawSz);
+            #pragma unused(SOSse) //dummy value used to increment file position
+            SOSahal = readByte(lRawRA, &lRawPos, lRawSz); //lower 4bits= pointtransform
+            SOSpttrans = SOSahal & 16;
+            if (verbose)
+                printf(" [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;
+    }
+    //NEXT: unpad data - delete byte that follows $FF
+    long lIncI = lRawPos; //input position
+    long lIncO = lRawPos; //output position
+    do {
+        lRawRA[lIncO] = lRawRA[lIncI];
+        if (lRawRA[lIncI] == 255) {
+            if (lRawRA[lIncI+1] == 0)
+                lIncI = lIncI+1;
+            else if (lRawRA[lIncI+1] == 0xD9)
+                lIncO = -666; //end of padding
+        }
+        lIncI++;
+        lIncO++;
+    } while (lIncO > 0);
+    //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 ++) {
+        for (int lInc = 0; lInc <= 17; lInc ++)
+            l[lFrameCount].SSSSszRA[lInc] = 123; //Impossible value for SSSS, suggests 8-bits can not describe answer
+        for (int lInc = 0; lInc <= 255; lInc ++)
+            l[lFrameCount].LookUpRA[lInc] = 255; //Impossible value for SSSS, suggests 8-bits can not describe answer
+    }
+    //NEXT: fill lookuptable
+    for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount ++) {
+        int lIncY = 0;
+        for (int lSz = 1; lSz <= 8; lSz ++) { //set the huffman lookup table for keys with lengths <=8
+            if (l[lFrameCount].DHTliRA[lSz]> 0) {
+                for (int lIncX = 1; lIncX <= l[lFrameCount].DHTliRA[lSz]; lIncX ++) {
+                    lIncY++;
+                    int lHufVal = l[lFrameCount].HufVal[lIncY]; //SSSS
+                    l[lFrameCount].SSSSszRA[lHufVal] = lSz;
+                    int k = (l[lFrameCount].HufCode[lIncY] << (8-lSz )) & 255; //K= most sig bits for hufman table
+                    if (lSz < 8) { //fill in all possible bits that exceed the huffman table
+                        int lInc = bitMask(8-lSz);
+                        for (int lCurrentBitPos = 0; lCurrentBitPos <= lInc; lCurrentBitPos++) {
+                            l[lFrameCount].LookUpRA[k+lCurrentBitPos] = lHufVal;
+                        }
+                    } 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);
+                } //Set SSSS
+            } //Length of size lInc > 0
+        } //for lInc := 1 to 8
+    } //For each frame, e.g. once each for Red/Green/Blue
+    //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values
+    if (lnHufTables < SOFnf) { //use single Hufman table for each frame
+        for (int lFrameCount = 2; lFrameCount <= SOFnf; lFrameCount++) {
+            l[lFrameCount] = l[1];
+        } //for each frame
+    } // if lnHufTables < SOFnf
+    //NEXT: uncompress data: different loops for different predictors
+    int lItems =  SOFxdim*SOFydim*SOFnf;
+    // lRawPos++;// <- only for Pascal where array is indexed from 1 not 0 first byte of data
+    int lCurrentBitPos = 0; //read in a new byte
+    //depending on SOSss, we see Table H.1
+    int lPredA = 0;
+    int lPredB = 0;
+    int lPredC = 0;
+    if (SOSss == 2) //predictor selection 2: above
+        lPredA = SOFxdim-1;
+    else if (SOSss == 3) //predictor selection 3: above+left
+        lPredA = SOFxdim;
+    else if ((SOSss == 4) || (SOSss == 5)) { //these use left, above and above+left WEIGHT LEFT
+        lPredA = 0; //Ra left
+        lPredB = SOFxdim-1; //Rb directly above
+        lPredC = SOFxdim; //Rc UpperLeft:above and to the left
+    } else if (SOSss == 6) { //also use left, above and above+left, WEIGHT ABOVE
+        lPredB = 0;
+        lPredA = SOFxdim-1; //Rb directly above
+        lPredC = SOFxdim; //Rc UpperLeft:above and to the left
+    }   else
+        lPredA = 0; //Ra: directly to left)
+    if (SOFprecision > 8) { //start - 16 bit data
+        *bits = 16;
+        int lPx = -1; //pixel position
+        int lPredicted =  1 << (SOFprecision-1-SOSpttrans);
+        lImgRA8 = (unsigned char*) malloc(lItems * 2);
+        uint16_t *lImgRA16 = (uint16_t*) lImgRA8;
+        for (int i = 0; i < lItems; i++)
+            lImgRA16[i] = 0; //zero array
+        int frame = 1;
+        for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor
+            lPx++; //writenext voxel
+            if (lIncX > 1) lPredicted = lImgRA16[lPx-1];
+            lImgRA16[lPx] = lPredicted+ decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+        }
+        for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows
+            lPx++; //write next voxel
+            lPredicted = lImgRA16[lPx-SOFxdim]; //use ABOVE
+            lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+            if (SOSss == 4) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA16[lPx-lPredA]+lImgRA16[lPx-lPredB]-lImgRA16[lPx-lPredC];
+                    lPx++; //writenext voxel
+                    lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+                } //for lIncX
+            } else if ((SOSss == 5) || (SOSss == 6)) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA16[lPx-lPredA]+ ((lImgRA16[lPx-lPredB]-lImgRA16[lPx-lPredC]) >> 1);
+                    lPx++; //writenext voxel
+                    lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+                } //for lIncX
+            } else if (SOSss == 7) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPx++; //writenext voxel
+                    lPredicted = (lImgRA16[lPx-1]+lImgRA16[lPx-SOFxdim]) >> 1;
+                    lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+                } //for lIncX
+            } else { //SOSss 1,2,3 read single values
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA16[lPx-lPredA];
+                    lPx++; //writenext voxel
+                    lImgRA16[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[frame]);
+                } //for lIncX
+            } // if..else possible predictors
+        }//for lIncY
+    } else if (SOFnf == 3) { //if 16-bit data; else 8-bit 3 frames
+        *bits = 8;
+        lImgRA8 = (unsigned char*) malloc(lItems );
+        int lPx[kmaxFrames+1], lPredicted[kmaxFrames+1]; //pixel position
+        for (int f = 1; f <= SOFnf; f++) {
+            lPx[f] = ((f-1) * (SOFxdim * SOFydim) ) -1;
+            lPredicted[f] = 1 << (SOFprecision-1-SOSpttrans);
+        }
+        for (int i = 0; i < lItems; i++)
+            lImgRA8[i] = 255; //zero array
+        for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor
+            for (int f = 1; f <= SOFnf; f++) {
+                lPx[f]++; //writenext voxel
+                if (lIncX > 1) lPredicted[f] = lImgRA8[lPx[f]-1];
+                lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+            }
+        } //first row always predicted by LEFT
+        for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows
+            for (int f = 1; f <= SOFnf; f++) {
+                lPx[f]++; //write next voxel
+                lPredicted[f] = lImgRA8[lPx[f]-SOFxdim]; //use ABOVE
+                lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+            }//first column of row always predicted by ABOVE
+            if (SOSss == 4) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    for (int f = 1; f <= SOFnf; f++) {
+                        lPredicted[f] = lImgRA8[lPx[f]-lPredA]+lImgRA8[lPx[f]-lPredB]-lImgRA8[lPx[f]-lPredC];
+                        lPx[f]++; //writenext voxel
+                        lImgRA8[lPx[f]] = lPredicted[f]+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+                    }
+                } //for lIncX
+            } else if ((SOSss == 5) || (SOSss == 6)) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    for (int f = 1; f <= SOFnf; f++) {
+                        lPredicted[f] = lImgRA8[lPx[f]-lPredA]+ ((lImgRA8[lPx[f]-lPredB]-lImgRA8[lPx[f]-lPredC]) >> 1);
+                        lPx[f]++; //writenext voxel
+                        lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+                    }
+                } //for lIncX
+            } else if (SOSss == 7) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    for (int f = 1; f <= SOFnf; f++) {
+                        lPx[f]++; //writenext voxel
+                        lPredicted[f] = (lImgRA8[lPx[f]-1]+lImgRA8[lPx[f]-SOFxdim]) >> 1;
+                        lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+                    }
+                } //for lIncX
+            } else { //SOSss 1,2,3 read single values
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    for (int f = 1; f <= SOFnf; f++) {
+                        lPredicted[f] = lImgRA8[lPx[f]-lPredA];
+                        lPx[f]++; //writenext voxel
+                        lImgRA8[lPx[f]] = lPredicted[f] + decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[f]);
+                    }
+                } //for lIncX
+            } // if..else possible predictors
+        }//for lIncY
+    } else { //if 8-bit data 3frames; else 8-bit 1 frames
+        *bits = 8;
+        lImgRA8 = (unsigned char*) malloc(lItems );
+        int lPx = -1; //pixel position
+        int lPredicted =  1 << (SOFprecision-1-SOSpttrans);
+        for (int i = 0; i < lItems; i++)
+            lImgRA8[i] = 0; //zero array
+        for (int lIncX = 1; lIncX <= SOFxdim; lIncX++) { //for first row - here we ALWAYS use LEFT as predictor
+            lPx++; //writenext voxel
+            if (lIncX > 1) lPredicted = lImgRA8[lPx-1];
+            int dx = decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+            lImgRA8[lPx] = lPredicted+dx;
+        }
+        for (int lIncY = 2; lIncY <= SOFydim; lIncY++) {//for all subsequent rows
+            lPx++; //write next voxel
+            lPredicted = lImgRA8[lPx-SOFxdim]; //use ABOVE
+            lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+            if (SOSss == 4) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA8[lPx-lPredA]+lImgRA8[lPx-lPredB]-lImgRA8[lPx-lPredC];
+                    lPx++; //writenext voxel
+                    lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+                } //for lIncX
+            } else if ((SOSss == 5) || (SOSss == 6)) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA8[lPx-lPredA]+ ((lImgRA8[lPx-lPredB]-lImgRA8[lPx-lPredC]) >> 1);
+                    lPx++; //writenext voxel
+                    lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+                } //for lIncX
+            } else if (SOSss == 7) {
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPx++; //writenext voxel
+                    lPredicted = (lImgRA8[lPx-1]+lImgRA8[lPx-SOFxdim]) >> 1;
+                    lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+                } //for lIncX
+            } else { //SOSss 1,2,3 read single values
+                for (int lIncX = 2; lIncX <= SOFxdim; lIncX++) {
+                    lPredicted = lImgRA8[lPx-lPredA];
+                    lPx++; //writenext voxel
+                    lImgRA8[lPx] = lPredicted+decodePixelDifference(lRawRA, &lRawPos, &lCurrentBitPos, l[1]);
+                } //for lIncX
+            } // if..else possible predictors
+        }//for lIncY
+    } //if 16bit else 8bit
+    free(lRawRA);
+    *dimX = SOFxdim;
+    *dimY = SOFydim;
+    *frames = SOFnf;
+    if (verbose)
+        printf("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes);
+    return lImgRA8;
+}// decode_JPEG_SOF_0XC3()
\ No newline at end of file
diff --git a/console/jpg_0XC3.h b/console/jpg_0XC3.h
new file mode 100644
index 0000000..0c92816
--- /dev/null
+++ b/console/jpg_0XC3.h
@@ -0,0 +1,23 @@
+//Decode DICOM Transfer Syntax 1.2.840.10008.1.2.4.70 and 1.2.840.10008.1.2.4.57
+// JPEG Lossless, Nonhierarchical
+// see ISO/IEC 10918-1 / ITU T.81 
+//  specifically, format with 'Start of Frame' (SOF) code 0xC3
+//  http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+// This code decodes data with 1..16 bits per pixel
+// It appears unique to medical imaging, and is not supported by most JPEG libraries
+// http://www.dicomlibrary.com/dicom/transfer-syntax/
+// https://en.wikipedia.org/wiki/Lossless_JPEG#Lossless_mode_of_operation
+#ifndef _JPEG_SOF_0XC3_
+#define _JPEG_SOF_0XC3_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+    
+unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/console/main_console.cpp b/console/main_console.cpp
new file mode 100644
index 0000000..b1ecc1d
--- /dev/null
+++ b/console/main_console.cpp
@@ -0,0 +1,211 @@
+//  main.m dcm2niix
+// by Chris Rorden on 3/22/14, see license.txt
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+
+//g++ -O3 main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix -lz
+
+//if you do not have zlib,you can compile without it
+// g++ -O3 -DmyDisableZLib main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -s -o dcm2niix
+//or you can build your own copy:
+// to compile you will first want to build the Z library, then compile the project
+// cd zlib-1.2.8
+// sudo ./configure;
+// sudo make
+
+
+//to generate combined 32-bit and 64-bit builds for OSX :
+// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch x86_64 -o dcm2niix64 -lz
+// g++ -O3 -x c++ main_console.c nii_dicom.c nifti1_io_core.c nii_ortho.c nii_dicom_batch.c -s -arch i386 -o dcm2niix32 -lz
+// lipo -create dcm2niix32 dcm2niix64 -o dcm2niix
+
+//On windows with mingw you may get "fatal error: zlib.h: No such file
+// to remedy, run "mingw-get install libz-dev" from mingw
+
+//Alternatively, windows users with VisualStudio can compile this project
+// 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
+
+
+//#define mydebugtest //automatically process directory specified in main, ignore input arguments
+
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+//#include <unistd.h>
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#include <stdio.h>
+#include "nii_dicom_batch.h"
+#include "nii_dicom.h"
+#include "nifti1_io_core.h"
+#include <math.h>
+
+const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe"
+    const char* pDelimeter = strrchr (path, '\\');
+    if (pDelimeter)
+        path = pDelimeter+1;
+    pDelimeter = strrchr (path, '/');
+    if (pDelimeter)
+        path = pDelimeter+1;
+    return path;
+} //removePath()
+
+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");
+    #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);
+    #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);
+    #endif
+    printf("  -h : show help\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("  -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("  -x : crop (y/n, default n)\n");
+    char gzCh = 'n';
+    if (opts.isGz) gzCh = 'y';
+    #ifdef myDisableZLib
+		if (strlen(opts.pigzname) > 0)
+			printf("  -z : gz compress images (y/n, default %c)\n", gzCh);
+		else
+			printf("  -z : gz compress images (y/n, default %c) [REQUIRES pigz]\n", gzCh);
+    #else
+		printf("  -z : gz compress images (y/i/n, default %c) [y=pigz, i=internal, n=no]\n", gzCh);
+    #endif
+
+#if defined(_WIN64) || defined(_WIN32)
+    printf(" Defaults stored in Windows registry\n");
+    printf(" Examples :\n");
+    printf("  %s c:\\DICOM\\dir\n", cstr);
+    printf("  %s -o c:\\out\\dir c:\\DICOM\\dir\n", cstr);
+    printf("  %s -f mystudy%%s c:\\DICOM\\dir\n", cstr);
+    printf("  %s -o \"c:\\dir with spaces\\dir\" c:\\dicomdir\n", cstr);
+#else
+    printf(" Defaults file : %s\n", opts.optsname);
+    printf(" Examples :\n");
+    printf("  %s /Users/chris/dir\n", cstr);
+    printf("  %s -o /users/cr/outdir/ -z y ~/dicomdir\n", cstr);
+    printf("  %s -f mystudy%%s ~/dicomdir\n", cstr);
+    printf("  %s -o \"~/dir with spaces/dir\" ~/dicomdir\n", cstr);
+#endif
+} //showHelp()
+
+//#define mydebugtest
+
+int main(int argc, const char * argv[])
+{
+    struct TDCMopts opts;
+    readIniFile(&opts, argv); //set default preferences
+#ifdef mydebugtest
+    //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/Philips_PARREC_Rotation/NoRotation/DBIEX_4_1.PAR");
+     //strcpy(opts.indir, "/Users/rorden/desktop/sliceOrder/dicom2/test");
+	 strcpy(opts.indir, "e:\\t1s");
+#else
+	#if defined(__APPLE__)
+		#define kOS "MacOS"
+	#elif (defined(__linux) || defined(__linux__))
+		#define kOS "Linux"
+	#else
+		#define kOS "Windows"
+	#endif
+    printf("Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n",kDCMvers, (unsigned long long) sizeof(size_t)*8, kOS);
+    if (argc < 2) {
+        showHelp(argv, opts);
+        return 0;
+    }
+    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)) {
+                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][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] == 's') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    opts.isOnlySingleFile = false;
+                else
+                    opts.isOnlySingleFile = true;
+            } else if ((argv[i][1] == 't') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    opts.isCreateText = false;
+                else
+                    opts.isCreateText = true;
+            } else if ((argv[i][1] == 'v') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0')) //0: verbose OFF
+                    opts.isVerbose = 0;
+                else if ((argv[i][0] == 'h') || (argv[i][0] == 'H')  || (argv[i][0] == '2')) //2: verbose HYPER
+                    opts.isVerbose = 2;
+                else
+                    opts.isVerbose = 1; //1: verbose ON
+            } else if ((argv[i][1] == 'x') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    opts.isCrop = false;
+                else
+                    opts.isCrop = true;
+            } else if ((argv[i][1] == 'z') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'i') || (argv[i][0] == 'I') ) {
+                    opts.isGz = true; //force use of internal compression instead of pigz
+                	strcpy(opts.pigzname,"");
+                } else if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    opts.isGz = false;
+                else
+                    opts.isGz = true;
+            } else if ((argv[i][1] == 'f') && ((i+1) < argc)) {
+                i++;
+                strcpy(opts.filename,argv[i]);
+            } else if ((argv[i][1] == 'o') && ((i+1) < argc)) {
+                i++;
+                isCustomOutDir = true;
+                strcpy(opts.outdir,argv[i]);
+            }
+            lastCommandArg = i;
+        } //if parameter is a command
+        i ++; //read next parameter
+    } //while parameters to read
+    //printf("%d %d",argc,lastCommandArg);
+    if (argc == (lastCommandArg+1))  { //+1 as array indexed from 0
+        //the user did not provide an input filename, report filename structure
+        char niiFilename[1024];
+        strcpy(opts.outdir,"");//no input supplied
+        nii_createDummyFilename(niiFilename, opts);
+        printf("%s\n",niiFilename);
+        return EXIT_SUCCESS;
+    }
+#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 (nii_loadDir(&opts) != EXIT_SUCCESS)
+    		return EXIT_FAILURE;
+    }
+    printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC);
+    saveIniFile(opts);
+    return EXIT_SUCCESS;
+}
diff --git a/console/main_console_batch.cpp b/console/main_console_batch.cpp
new file mode 100644
index 0000000..0230554
--- /dev/null
+++ b/console/main_console_batch.cpp
@@ -0,0 +1,80 @@
+//  main.m dcm2niix
+// by Chris Rorden on 3/22/14, see license.txt
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//  yaml batch suport by Benjamin Irving, 2016 - maintains copyright
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#include <stdio.h>
+#include <cmath>
+#include <string>
+#include <iostream>
+
+#include "nii_dicom_batch.h"
+
+#include <fstream>
+#include <yaml-cpp/yaml.h>
+
+const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe"
+    const char* pDelimeter = strrchr (path, '\\');
+    if (pDelimeter)
+        path = pDelimeter+1;
+    pDelimeter = strrchr (path, '/');
+    if (pDelimeter)
+        path = pDelimeter+1;
+    return path;
+} //removePath()
+
+
+int rmainbatch(TDCMopts opts)
+{
+    printf("Chris Rorden's dcm2niiX version %s (%lu-bit)\n",kDCMvers, sizeof(size_t)*8);
+
+    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[])
+{
+
+    if (argc != 2) {
+        std::cout << "Do not include additional inputs with a config file \n";
+        throw;
+    }
+
+    // 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>();
+    opts.isOnlySingleFile = config["Options"]["isOnlySingleFile"].as<bool>();
+    opts.isCreateText = false;
+    opts.isVerbose = 0;
+    opts.isGz = config["Options"]["isGz"].as<bool>(); //save data as compressed (.nii.gz) or raw (.nii)
+   	/*bool isInternalGz = config["Options"]["isInternalGz"].as<bool>();
+    if (isInternalGz) {
+        strcpy(opts.pigzname, "”); //do NOT use pigz: force internal compressor
+		//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;
+}
diff --git a/console/makefile b/console/makefile
new file mode 100644
index 0000000..3cf9f9a
--- /dev/null
+++ b/console/makefile
@@ -0,0 +1,9 @@
+CFLAGS=-s
+ifneq ($(OS),Windows_NT)
+	OS = $(shell uname)
+ 	ifeq "$(OS)" "Darwin"
+		CFLAGS=-dead_strip
+	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
diff --git a/console/miniz.c b/console/miniz.c
new file mode 100644
index 0000000..fa473b2
--- /dev/null
+++ b/console/miniz.c
@@ -0,0 +1,4919 @@
+/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
+   See "unlicense" statement at the end of this file.
+   Rich Geldreich <richgel99 at gmail.com>, last updated Oct. 13, 2013
+   Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
+
+   Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
+   MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
+
+   * Change History
+     10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!):
+       - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon at hp.com) which could cause locate files to not find files. This bug
+        would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place()
+        (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag).
+       - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size
+       - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries.
+         Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice).
+       - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes
+       - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed
+       - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6.
+       - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti
+       - Merged MZ_FORCEINLINE fix from hdeanclark
+       - Fix <time.h> include before config #ifdef, thanks emil.brink
+       - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can
+        set it to 1 for real-time compression).
+       - Merged in some compiler fixes from paulharris's github repro.
+       - Retested this build under Windows (VS 2010, including static analysis), tcc  0.9.26, gcc v4.6 and clang v3.3.
+       - Added example6.c, which dumps an image of the mandelbrot set to a PNG file.
+       - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more.
+       - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled
+       - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch
+     5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
+     5/19/12 v1.13 - From jason at cornsyrup.org and kelwert at mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
+       - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
+       - Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
+       - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
+        "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
+       - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
+       - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
+       - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
+       - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
+       - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
+     4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
+      level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced at valvesoftware.com> for the feedback/bug report.
+     5/28/11 v1.11 - Added statement from unlicense.org
+     5/27/11 v1.10 - Substantial compressor optimizations:
+      - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
+      - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
+      - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
+      - Refactored the compression code for better readability and maintainability.
+      - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
+       drop in throughput on some files).
+     5/15/11 v1.09 - Initial stable release.
+
+   * Low-level Deflate/Inflate implementation notes:
+
+     Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
+     greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
+     approximately as well as zlib.
+
+     Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
+     coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
+     block large enough to hold the entire file.
+
+     The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
+
+   * zlib-style API notes:
+
+     miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
+     zlib replacement in many apps:
+        The z_stream struct, optional memory allocation callbacks
+        deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
+        inflateInit/inflateInit2/inflate/inflateEnd
+        compress, compress2, compressBound, uncompress
+        CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
+        Supports raw deflate streams or standard zlib streams with adler-32 checking.
+
+     Limitations:
+      The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
+      I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
+      there are no guarantees that miniz.c pulls this off perfectly.
+
+   * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
+     Alex Evans. Supports 1-4 bytes/pixel images.
+
+   * ZIP archive API notes:
+
+     The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
+     get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
+     existing archives, create new archives, append new files to existing archives, or clone archive data from
+     one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
+     or you can specify custom file read/write callbacks.
+
+     - Archive reading: Just call this function to read a single file from a disk archive:
+
+      void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
+        size_t *pSize, mz_uint zip_flags);
+
+     For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
+     directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
+
+     - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
+
+     int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
+
+     The locate operation can optionally check file comments too, which (as one example) can be used to identify
+     multiple versions of the same file in an archive. This function uses a simple linear search through the central
+     directory, so it's not very fast.
+
+     Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
+     retrieve detailed info on each file by calling mz_zip_reader_file_stat().
+
+     - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
+     to disk and builds an exact image of the central directory in memory. The central directory image is written
+     all at once at the end of the archive file when the archive is finalized.
+
+     The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
+     which can be useful when the archive will be read from optical media. Also, the writer supports placing
+     arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
+     readable by any ZIP tool.
+
+     - Archive appending: The simple way to add a single file to an archive is to call this function:
+
+      mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
+        const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+
+     The archive will be created if it doesn't already exist, otherwise it'll be appended to.
+     Note the appending is done in-place and is not an atomic operation, so if something goes wrong
+     during the operation it's possible the archive could be left without a central directory (although the local
+     file headers and file data will be fine, so the archive will be recoverable).
+
+     For more complex archive modification scenarios:
+     1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
+     preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
+     compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
+     you're done. This is safe but requires a bunch of temporary disk space or heap memory.
+
+     2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
+     append new files as needed, then finalize the archive which will write an updated central directory to the
+     original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
+     possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
+
+     - ZIP archive support limitations:
+     No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
+     Requires streams capable of seeking.
+
+   * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
+     below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
+
+   * Important: For best perf. be sure to customize the below macros for your target platform:
+     #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
+     #define MINIZ_LITTLE_ENDIAN 1
+     #define MINIZ_HAS_64BIT_REGISTERS 1
+
+   * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz
+     uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files
+     (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).
+*/
+
+#ifndef MINIZ_HEADER_INCLUDED
+#define MINIZ_HEADER_INCLUDED
+
+#include <stdlib.h>
+
+// Defines to completely disable specific portions of miniz.c:
+// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
+
+// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
+//#define MINIZ_NO_STDIO
+
+// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
+// get/set file times, and the C run-time funcs that get/set times won't be called.
+// The current downside is the times written to your archives will be from 1979.
+//#define MINIZ_NO_TIME
+
+// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
+//#define MINIZ_NO_ARCHIVE_APIS
+
+// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
+//#define MINIZ_NO_ARCHIVE_WRITING_APIS
+
+// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
+//#define MINIZ_NO_ZLIB_APIS
+
+// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
+//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+
+// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
+// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
+// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
+// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
+//#define MINIZ_NO_MALLOC
+
+#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))
+  // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux
+  #define MINIZ_NO_TIME
+#endif
+
+#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
+  #include <time.h>
+#endif
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
+// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
+#define MINIZ_X86_OR_X64_CPU 1
+#endif
+
+#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
+// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
+#define MINIZ_LITTLE_ENDIAN 1
+#endif
+
+#if MINIZ_X86_OR_X64_CPU
+// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
+#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
+#endif
+
+#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
+// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
+#define MINIZ_HAS_64BIT_REGISTERS 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ------------------- zlib-style API Definitions.
+
+// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
+typedef unsigned long mz_ulong;
+
+// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
+void mz_free(void *p);
+
+#define MZ_ADLER32_INIT (1)
+// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
+mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
+
+#define MZ_CRC32_INIT (0)
+// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
+mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
+
+// Compression strategies.
+enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
+
+// Method
+#define MZ_DEFLATED 8
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+// Heap allocation callbacks.
+// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
+typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
+typedef void (*mz_free_func)(void *opaque, void *address);
+typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
+
+#define MZ_VERSION          "9.1.15"
+#define MZ_VERNUM           0x91F0
+#define MZ_VER_MAJOR        9
+#define MZ_VER_MINOR        1
+#define MZ_VER_REVISION     15
+#define MZ_VER_SUBREVISION  0
+
+// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
+enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
+
+// Return status codes. MZ_PARAM_ERROR is non-standard.
+enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
+
+// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
+enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
+
+// Window bits
+#define MZ_DEFAULT_WINDOW_BITS 15
+
+struct mz_internal_state;
+
+// Compression/decompression stream struct.
+typedef struct mz_stream_s
+{
+  const unsigned char *next_in;     // pointer to next byte to read
+  unsigned int avail_in;            // number of bytes available at next_in
+  mz_ulong total_in;                // total number of bytes consumed so far
+
+  unsigned char *next_out;          // pointer to next byte to write
+  unsigned int avail_out;           // number of bytes that can be written to next_out
+  mz_ulong total_out;               // total number of bytes produced so far
+
+  char *msg;                        // error msg (unused)
+  struct mz_internal_state *state;  // internal state, allocated by zalloc/zfree
+
+  mz_alloc_func zalloc;             // optional heap allocation function (defaults to malloc)
+  mz_free_func zfree;               // optional heap free function (defaults to free)
+  void *opaque;                     // heap alloc function user pointer
+
+  int data_type;                    // data_type (unused)
+  mz_ulong adler;                   // adler32 of the source or uncompressed data
+  mz_ulong reserved;                // not used
+} mz_stream;
+
+typedef mz_stream *mz_streamp;
+
+// Returns the version string of miniz.c.
+const char *mz_version(void);
+
+// mz_deflateInit() initializes a compressor with default options:
+// Parameters:
+//  pStream must point to an initialized mz_stream struct.
+//  level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
+//  level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
+//  (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
+// Return values:
+//  MZ_OK on success.
+//  MZ_STREAM_ERROR if the stream is bogus.
+//  MZ_PARAM_ERROR if the input parameters are bogus.
+//  MZ_MEM_ERROR on out of memory.
+int mz_deflateInit(mz_streamp pStream, int level);
+
+// mz_deflateInit2() is like mz_deflate(), except with more control:
+// Additional parameters:
+//   method must be MZ_DEFLATED
+//   window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
+//   mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
+int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
+
+// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
+int mz_deflateReset(mz_streamp pStream);
+
+// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
+// Parameters:
+//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
+//   flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
+// Return values:
+//   MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
+//   MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
+//   MZ_STREAM_ERROR if the stream is bogus.
+//   MZ_PARAM_ERROR if one of the parameters is invalid.
+//   MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
+int mz_deflate(mz_streamp pStream, int flush);
+
+// mz_deflateEnd() deinitializes a compressor:
+// Return values:
+//  MZ_OK on success.
+//  MZ_STREAM_ERROR if the stream is bogus.
+int mz_deflateEnd(mz_streamp pStream);
+
+// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
+mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
+
+// Single-call compression functions mz_compress() and mz_compress2():
+// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
+int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
+
+// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
+mz_ulong mz_compressBound(mz_ulong source_len);
+
+// Initializes a decompressor.
+int mz_inflateInit(mz_streamp pStream);
+
+// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
+// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
+int mz_inflateInit2(mz_streamp pStream, int window_bits);
+
+// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
+// Parameters:
+//   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
+//   flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
+//   On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
+//   MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
+// Return values:
+//   MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
+//   MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
+//   MZ_STREAM_ERROR if the stream is bogus.
+//   MZ_DATA_ERROR if the deflate stream is invalid.
+//   MZ_PARAM_ERROR if one of the parameters is invalid.
+//   MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
+//   with more input data, or with more room in the output buffer (except when using single call decompression, described above).
+int mz_inflate(mz_streamp pStream, int flush);
+
+// Deinitializes a decompressor.
+int mz_inflateEnd(mz_streamp pStream);
+
+// Single-call decompression.
+// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
+int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
+
+// Returns a string description of the specified error code, or NULL if the error code is invalid.
+const char *mz_error(int err);
+
+// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
+// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
+#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+  typedef unsigned char Byte;
+  typedef unsigned int uInt;
+  typedef mz_ulong uLong;
+  typedef Byte Bytef;
+  typedef uInt uIntf;
+  typedef char charf;
+  typedef int intf;
+  typedef void *voidpf;
+  typedef uLong uLongf;
+  typedef void *voidp;
+  typedef void *const voidpc;
+  #define Z_NULL                0
+  #define Z_NO_FLUSH            MZ_NO_FLUSH
+  #define Z_PARTIAL_FLUSH       MZ_PARTIAL_FLUSH
+  #define Z_SYNC_FLUSH          MZ_SYNC_FLUSH
+  #define Z_FULL_FLUSH          MZ_FULL_FLUSH
+  #define Z_FINISH              MZ_FINISH
+  #define Z_BLOCK               MZ_BLOCK
+  #define Z_OK                  MZ_OK
+  #define Z_STREAM_END          MZ_STREAM_END
+  #define Z_NEED_DICT           MZ_NEED_DICT
+  #define Z_ERRNO               MZ_ERRNO
+  #define Z_STREAM_ERROR        MZ_STREAM_ERROR
+  #define Z_DATA_ERROR          MZ_DATA_ERROR
+  #define Z_MEM_ERROR           MZ_MEM_ERROR
+  #define Z_BUF_ERROR           MZ_BUF_ERROR
+  #define Z_VERSION_ERROR       MZ_VERSION_ERROR
+  #define Z_PARAM_ERROR         MZ_PARAM_ERROR
+  #define Z_NO_COMPRESSION      MZ_NO_COMPRESSION
+  #define Z_BEST_SPEED          MZ_BEST_SPEED
+  #define Z_BEST_COMPRESSION    MZ_BEST_COMPRESSION
+  #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
+  #define Z_DEFAULT_STRATEGY    MZ_DEFAULT_STRATEGY
+  #define Z_FILTERED            MZ_FILTERED
+  #define Z_HUFFMAN_ONLY        MZ_HUFFMAN_ONLY
+  #define Z_RLE                 MZ_RLE
+  #define Z_FIXED               MZ_FIXED
+  #define Z_DEFLATED            MZ_DEFLATED
+  #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
+  #define alloc_func            mz_alloc_func
+  #define free_func             mz_free_func
+  #define internal_state        mz_internal_state
+  #define z_stream              mz_stream
+  #define deflateInit           mz_deflateInit
+  #define deflateInit2          mz_deflateInit2
+  #define deflateReset          mz_deflateReset
+  #define deflate               mz_deflate
+  #define deflateEnd            mz_deflateEnd
+  #define deflateBound          mz_deflateBound
+  #define compress              mz_compress
+  #define compress2             mz_compress2
+  #define compressBound         mz_compressBound
+  #define inflateInit           mz_inflateInit
+  #define inflateInit2          mz_inflateInit2
+  #define inflate               mz_inflate
+  #define inflateEnd            mz_inflateEnd
+  #define uncompress            mz_uncompress
+  #define crc32                 mz_crc32
+  #define adler32               mz_adler32
+  #define MAX_WBITS             15
+  #define MAX_MEM_LEVEL         9
+  #define zError                mz_error
+  #define ZLIB_VERSION          MZ_VERSION
+  #define ZLIB_VERNUM           MZ_VERNUM
+  #define ZLIB_VER_MAJOR        MZ_VER_MAJOR
+  #define ZLIB_VER_MINOR        MZ_VER_MINOR
+  #define ZLIB_VER_REVISION     MZ_VER_REVISION
+  #define ZLIB_VER_SUBREVISION  MZ_VER_SUBREVISION
+  #define zlibVersion           mz_version
+  #define zlib_version          mz_version()
+#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
+
+#endif // MINIZ_NO_ZLIB_APIS
+
+// ------------------- Types and macros
+
+typedef unsigned char mz_uint8;
+typedef signed short mz_int16;
+typedef unsigned short mz_uint16;
+typedef unsigned int mz_uint32;
+typedef unsigned int mz_uint;
+typedef long long mz_int64;
+typedef unsigned long long mz_uint64;
+typedef int mz_bool;
+
+#define MZ_FALSE (0)
+#define MZ_TRUE (1)
+
+// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message.
+#ifdef _MSC_VER
+   #define MZ_MACRO_END while (0, 0)
+#else
+   #define MZ_MACRO_END while (0)
+#endif
+
+// ------------------- ZIP archive reading/writing
+
+#ifndef MINIZ_NO_ARCHIVE_APIS
+
+enum
+{
+  MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
+  MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
+  MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
+};
+
+typedef struct
+{
+  mz_uint32 m_file_index;
+  mz_uint32 m_central_dir_ofs;
+  mz_uint16 m_version_made_by;
+  mz_uint16 m_version_needed;
+  mz_uint16 m_bit_flag;
+  mz_uint16 m_method;
+#ifndef MINIZ_NO_TIME
+  time_t m_time;
+#endif
+  mz_uint32 m_crc32;
+  mz_uint64 m_comp_size;
+  mz_uint64 m_uncomp_size;
+  mz_uint16 m_internal_attr;
+  mz_uint32 m_external_attr;
+  mz_uint64 m_local_header_ofs;
+  mz_uint32 m_comment_size;
+  char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
+  char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
+} mz_zip_archive_file_stat;
+
+typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
+typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
+
+struct mz_zip_internal_state_tag;
+typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
+
+typedef enum
+{
+  MZ_ZIP_MODE_INVALID = 0,
+  MZ_ZIP_MODE_READING = 1,
+  MZ_ZIP_MODE_WRITING = 2,
+  MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
+} mz_zip_mode;
+
+typedef struct mz_zip_archive_tag
+{
+  mz_uint64 m_archive_size;
+  mz_uint64 m_central_directory_file_ofs;
+  mz_uint m_total_files;
+  mz_zip_mode m_zip_mode;
+
+  mz_uint m_file_offset_alignment;
+
+  mz_alloc_func m_pAlloc;
+  mz_free_func m_pFree;
+  mz_realloc_func m_pRealloc;
+  void *m_pAlloc_opaque;
+
+  mz_file_read_func m_pRead;
+  mz_file_write_func m_pWrite;
+  void *m_pIO_opaque;
+
+  mz_zip_internal_state *m_pState;
+
+} mz_zip_archive;
+
+typedef enum
+{
+  MZ_ZIP_FLAG_CASE_SENSITIVE                = 0x0100,
+  MZ_ZIP_FLAG_IGNORE_PATH                   = 0x0200,
+  MZ_ZIP_FLAG_COMPRESSED_DATA               = 0x0400,
+  MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
+} mz_zip_flags;
+
+// ZIP archive reading
+
+// Inits a ZIP archive reader.
+// These functions read and validate the archive's central directory.
+mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
+mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
+#endif
+
+// Returns the total number of files in the archive.
+mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
+
+// Returns detailed information about an archive file entry.
+mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
+
+// Determines if an archive file entry is a directory entry.
+mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
+mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
+
+// Retrieves the filename of an archive file entry.
+// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
+mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
+
+// Attempts to locates a file in the archive's central directory.
+// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
+// Returns -1 if the file cannot be found.
+int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
+
+// Extracts a archive file to a memory buffer using no memory allocation.
+mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
+mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
+
+// Extracts a archive file to a memory buffer.
+mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
+mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
+
+// Extracts a archive file to a dynamically allocated heap buffer.
+void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
+void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
+
+// Extracts a archive file using a callback function to output the file's data.
+mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
+mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
+
+#ifndef MINIZ_NO_STDIO
+// Extracts a archive file to a disk file and sets its last accessed and modified times.
+// This function only extracts files, not archive directory records.
+mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
+mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
+#endif
+
+// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
+mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
+
+// ZIP archive writing
+
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+// Inits a ZIP archive writer.
+mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
+mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
+#endif
+
+// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
+// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
+// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
+// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
+// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
+// the archive is finalized the file's central directory will be hosed.
+mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
+
+// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
+// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
+// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
+mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
+mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
+
+#ifndef MINIZ_NO_STDIO
+// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
+// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
+mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+#endif
+
+// Adds a file to an archive by fully cloning the data from another archive.
+// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
+mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
+
+// Finalizes the archive by writing the central directory records followed by the end of central directory record.
+// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
+// An archive must be manually finalized by calling this function for it to be valid.
+mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
+mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
+
+// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
+// Note for the archive to be valid, it must have been finalized before ending.
+mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
+
+// Misc. high-level helper functions:
+
+// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
+// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
+mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
+
+// Reads a single file from an archive into a heap block.
+// Returns NULL on failure.
+void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags);
+
+#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
+
+// ------------------- Low-level Decompression API Definitions
+
+// Decompression flags used by tinfl_decompress().
+// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
+// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
+// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
+// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
+enum
+{
+  TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
+  TINFL_FLAG_HAS_MORE_INPUT = 2,
+  TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
+  TINFL_FLAG_COMPUTE_ADLER32 = 8
+};
+
+// High level decompression functions:
+// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
+// On entry:
+//  pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
+// On return:
+//  Function returns a pointer to the decompressed data, or NULL on failure.
+//  *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
+//  The caller must call mz_free() on the returned block when it's no longer needed.
+void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
+// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
+#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
+size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
+// Returns 1 on success or 0 on failure.
+typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
+int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
+
+// Max size of LZ dictionary.
+#define TINFL_LZ_DICT_SIZE 32768
+
+// Return status.
+typedef enum
+{
+  TINFL_STATUS_BAD_PARAM = -3,
+  TINFL_STATUS_ADLER32_MISMATCH = -2,
+  TINFL_STATUS_FAILED = -1,
+  TINFL_STATUS_DONE = 0,
+  TINFL_STATUS_NEEDS_MORE_INPUT = 1,
+  TINFL_STATUS_HAS_MORE_OUTPUT = 2
+} tinfl_status;
+
+// Initializes the decompressor to its initial state.
+#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
+#define tinfl_get_adler32(r) (r)->m_check_adler32
+
+// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
+// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
+tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
+
+// Internal/private bits follow.
+enum
+{
+  TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
+  TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
+};
+
+typedef struct
+{
+  mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
+  mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
+} tinfl_huff_table;
+
+#if MINIZ_HAS_64BIT_REGISTERS
+  #define TINFL_USE_64BIT_BITBUF 1
+#endif
+
+#if TINFL_USE_64BIT_BITBUF
+  typedef mz_uint64 tinfl_bit_buf_t;
+  #define TINFL_BITBUF_SIZE (64)
+#else
+  typedef mz_uint32 tinfl_bit_buf_t;
+  #define TINFL_BITBUF_SIZE (32)
+#endif
+
+struct tinfl_decompressor_tag
+{
+  mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
+  tinfl_bit_buf_t m_bit_buf;
+  size_t m_dist_from_out_buf_start;
+  tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
+  mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
+};
+
+// ------------------- Low-level Compression API Definitions
+
+// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
+#define TDEFL_LESS_MEMORY 0
+
+// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
+// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
+enum
+{
+  TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
+};
+
+// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
+// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
+// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
+// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
+// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
+// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
+// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
+// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
+// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK).
+enum
+{
+  TDEFL_WRITE_ZLIB_HEADER             = 0x01000,
+  TDEFL_COMPUTE_ADLER32               = 0x02000,
+  TDEFL_GREEDY_PARSING_FLAG           = 0x04000,
+  TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
+  TDEFL_RLE_MATCHES                   = 0x10000,
+  TDEFL_FILTER_MATCHES                = 0x20000,
+  TDEFL_FORCE_ALL_STATIC_BLOCKS       = 0x40000,
+  TDEFL_FORCE_ALL_RAW_BLOCKS          = 0x80000
+};
+
+// High level compression functions:
+// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
+// On entry:
+//  pSrc_buf, src_buf_len: Pointer and size of source block to compress.
+//  flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
+// On return:
+//  Function returns a pointer to the compressed data, or NULL on failure.
+//  *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
+//  The caller must free() the returned block when it's no longer needed.
+void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
+
+// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
+// Returns 0 on failure.
+size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
+
+// Compresses an image to a compressed PNG file in memory.
+// On entry:
+//  pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. 
+//  The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
+//  level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL
+//  If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps).
+// On return:
+//  Function returns a pointer to the compressed data, or NULL on failure.
+//  *pLen_out will be set to the size of the PNG image file.
+//  The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
+void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);
+void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
+
+// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
+typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
+
+// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
+mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
+
+// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
+#if TDEFL_LESS_MEMORY
+enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
+#else
+enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
+#endif
+
+// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
+typedef enum
+{
+  TDEFL_STATUS_BAD_PARAM = -2,
+  TDEFL_STATUS_PUT_BUF_FAILED = -1,
+  TDEFL_STATUS_OKAY = 0,
+  TDEFL_STATUS_DONE = 1,
+} tdefl_status;
+
+// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
+typedef enum
+{
+  TDEFL_NO_FLUSH = 0,
+  TDEFL_SYNC_FLUSH = 2,
+  TDEFL_FULL_FLUSH = 3,
+  TDEFL_FINISH = 4
+} tdefl_flush;
+
+// tdefl's compression state structure.
+typedef struct
+{
+  tdefl_put_buf_func_ptr m_pPut_buf_func;
+  void *m_pPut_buf_user;
+  mz_uint m_flags, m_max_probes[2];
+  int m_greedy_parsing;
+  mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
+  mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
+  mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
+  mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
+  tdefl_status m_prev_return_status;
+  const void *m_pIn_buf;
+  void *m_pOut_buf;
+  size_t *m_pIn_buf_size, *m_pOut_buf_size;
+  tdefl_flush m_flush;
+  const mz_uint8 *m_pSrc;
+  size_t m_src_buf_left, m_out_buf_ofs;
+  mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
+  mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+  mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+  mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
+  mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
+  mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
+  mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
+  mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
+} tdefl_compressor;
+
+// Initializes the compressor.
+// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
+// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
+// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
+// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
+tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
+
+// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
+tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
+
+// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
+// tdefl_compress_buffer() always consumes the entire input buffer.
+tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
+
+tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
+mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
+
+// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
+#ifndef MINIZ_NO_ZLIB_APIS
+// Create tdefl_compress() flags given zlib-style compression parameters.
+// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
+// window_bits may be -15 (raw deflate) or 15 (zlib)
+// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
+mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
+#endif // #ifndef MINIZ_NO_ZLIB_APIS
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MINIZ_HEADER_INCLUDED
+
+// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
+
+#ifndef MINIZ_HEADER_FILE_ONLY
+
+typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1];
+typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1];
+typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1];
+
+#include <string.h>
+#include <assert.h>
+
+#define MZ_ASSERT(x) assert(x)
+
+#ifdef MINIZ_NO_MALLOC
+  #define MZ_MALLOC(x) NULL
+  #define MZ_FREE(x) (void)x, ((void)0)
+  #define MZ_REALLOC(p, x) NULL
+#else
+  #define MZ_MALLOC(x) malloc(x)
+  #define MZ_FREE(x) free(x)
+  #define MZ_REALLOC(p, x) realloc(p, x)
+#endif
+
+#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
+#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
+#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+  #define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
+  #define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
+#else
+  #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
+  #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
+#endif
+
+#ifdef _MSC_VER
+  #define MZ_FORCEINLINE __forceinline
+#elif defined(__GNUC__)
+  #define MZ_FORCEINLINE inline __attribute__((__always_inline__))
+#else
+  #define MZ_FORCEINLINE inline
+#endif
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+// ------------------- zlib-style API's
+
+mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)
+{
+  mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552;
+  if (!ptr) return MZ_ADLER32_INIT;
+  while (buf_len) {
+    for (i = 0; i + 7 < block_len; i += 8, ptr += 8) {
+      s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
+      s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
+    }
+    for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
+    s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
+  }
+  return (s2 << 16) + s1;
+}
+
+// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/
+mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)
+{
+  static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };
+  mz_uint32 crcu32 = (mz_uint32)crc;
+  if (!ptr) return MZ_CRC32_INIT;
+  crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; }
+  return ~crcu32;
+}
+
+void mz_free(void *p)
+{
+  MZ_FREE(p);
+}
+
+#ifndef MINIZ_NO_ZLIB_APIS
+
+static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); }
+static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); }
+static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); }
+
+const char *mz_version(void)
+{
+  return MZ_VERSION;
+}
+
+int mz_deflateInit(mz_streamp pStream, int level)
+{
+  return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);
+}
+
+int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)
+{
+  tdefl_compressor *pComp;
+  mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);
+
+  if (!pStream) return MZ_STREAM_ERROR;
+  if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR;
+
+  pStream->data_type = 0;
+  pStream->adler = MZ_ADLER32_INIT;
+  pStream->msg = NULL;
+  pStream->reserved = 0;
+  pStream->total_in = 0;
+  pStream->total_out = 0;
+  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
+  if (!pStream->zfree) pStream->zfree = def_free_func;
+
+  pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));
+  if (!pComp)
+    return MZ_MEM_ERROR;
+
+  pStream->state = (struct mz_internal_state *)pComp;
+
+  if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)
+  {
+    mz_deflateEnd(pStream);
+    return MZ_PARAM_ERROR;
+  }
+
+  return MZ_OK;
+}
+
+int mz_deflateReset(mz_streamp pStream)
+{
+  if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR;
+  pStream->total_in = pStream->total_out = 0;
+  tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags);
+  return MZ_OK;
+}
+
+int mz_deflate(mz_streamp pStream, int flush)
+{
+  size_t in_bytes, out_bytes;
+  mz_ulong orig_total_in, orig_total_out;
+  int mz_status = MZ_OK;
+
+  if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR;
+  if (!pStream->avail_out) return MZ_BUF_ERROR;
+
+  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
+
+  if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)
+    return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;
+
+  orig_total_in = pStream->total_in; orig_total_out = pStream->total_out;
+  for ( ; ; )
+  {
+    tdefl_status defl_status;
+    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
+
+    defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);
+    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
+    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state);
+
+    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes;
+    pStream->total_out += (mz_uint)out_bytes;
+
+    if (defl_status < 0)
+    {
+      mz_status = MZ_STREAM_ERROR;
+      break;
+    }
+    else if (defl_status == TDEFL_STATUS_DONE)
+    {
+      mz_status = MZ_STREAM_END;
+      break;
+    }
+    else if (!pStream->avail_out)
+      break;
+    else if ((!pStream->avail_in) && (flush != MZ_FINISH))
+    {
+      if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))
+        break;
+      return MZ_BUF_ERROR; // Can't make forward progress without some input.
+    }
+  }
+  return mz_status;
+}
+
+int mz_deflateEnd(mz_streamp pStream)
+{
+  if (!pStream) return MZ_STREAM_ERROR;
+  if (pStream->state)
+  {
+    pStream->zfree(pStream->opaque, pStream->state);
+    pStream->state = NULL;
+  }
+  return MZ_OK;
+}
+
+mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)
+{
+  (void)pStream;
+  // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.)
+  return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);
+}
+
+int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)
+{
+  int status;
+  mz_stream stream;
+  memset(&stream, 0, sizeof(stream));
+
+  // In case mz_ulong is 64-bits (argh I hate longs).
+  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
+
+  stream.next_in = pSource;
+  stream.avail_in = (mz_uint32)source_len;
+  stream.next_out = pDest;
+  stream.avail_out = (mz_uint32)*pDest_len;
+
+  status = mz_deflateInit(&stream, level);
+  if (status != MZ_OK) return status;
+
+  status = mz_deflate(&stream, MZ_FINISH);
+  if (status != MZ_STREAM_END)
+  {
+    mz_deflateEnd(&stream);
+    return (status == MZ_OK) ? MZ_BUF_ERROR : status;
+  }
+
+  *pDest_len = stream.total_out;
+  return mz_deflateEnd(&stream);
+}
+
+int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+  return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);
+}
+
+mz_ulong mz_compressBound(mz_ulong source_len)
+{
+  return mz_deflateBound(NULL, source_len);
+}
+
+typedef struct
+{
+  tinfl_decompressor m_decomp;
+  mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits;
+  mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];
+  tinfl_status m_last_status;
+} inflate_state;
+
+int mz_inflateInit2(mz_streamp pStream, int window_bits)
+{
+  inflate_state *pDecomp;
+  if (!pStream) return MZ_STREAM_ERROR;
+  if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR;
+
+  pStream->data_type = 0;
+  pStream->adler = 0;
+  pStream->msg = NULL;
+  pStream->total_in = 0;
+  pStream->total_out = 0;
+  pStream->reserved = 0;
+  if (!pStream->zalloc) pStream->zalloc = def_alloc_func;
+  if (!pStream->zfree) pStream->zfree = def_free_func;
+
+  pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));
+  if (!pDecomp) return MZ_MEM_ERROR;
+
+  pStream->state = (struct mz_internal_state *)pDecomp;
+
+  tinfl_init(&pDecomp->m_decomp);
+  pDecomp->m_dict_ofs = 0;
+  pDecomp->m_dict_avail = 0;
+  pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;
+  pDecomp->m_first_call = 1;
+  pDecomp->m_has_flushed = 0;
+  pDecomp->m_window_bits = window_bits;
+
+  return MZ_OK;
+}
+
+int mz_inflateInit(mz_streamp pStream)
+{
+   return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);
+}
+
+int mz_inflate(mz_streamp pStream, int flush)
+{
+  inflate_state* pState;
+  mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;
+  size_t in_bytes, out_bytes, orig_avail_in;
+  tinfl_status status;
+
+  if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR;
+  if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH;
+  if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
+
+  pState = (inflate_state*)pStream->state;
+  if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
+  orig_avail_in = pStream->avail_in;
+
+  first_call = pState->m_first_call; pState->m_first_call = 0;
+  if (pState->m_last_status < 0) return MZ_DATA_ERROR;
+
+  if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR;
+  pState->m_has_flushed |= (flush == MZ_FINISH);
+
+  if ((flush == MZ_FINISH) && (first_call))
+  {
+    // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file.
+    decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;
+    in_bytes = pStream->avail_in; out_bytes = pStream->avail_out;
+    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);
+    pState->m_last_status = status;
+    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes;
+    pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+    pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes;
+
+    if (status < 0)
+      return MZ_DATA_ERROR;
+    else if (status != TINFL_STATUS_DONE)
+    {
+      pState->m_last_status = TINFL_STATUS_FAILED;
+      return MZ_BUF_ERROR;
+    }
+    return MZ_STREAM_END;
+  }
+  // flush != MZ_FINISH then we must assume there's more input.
+  if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;
+
+  if (pState->m_dict_avail)
+  {
+    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
+    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+    return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+  }
+
+  for ( ; ; )
+  {
+    in_bytes = pStream->avail_in;
+    out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;
+
+    status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);
+    pState->m_last_status = status;
+
+    pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes;
+    pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp);
+
+    pState->m_dict_avail = (mz_uint)out_bytes;
+
+    n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);
+    memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);
+    pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n;
+    pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);
+
+    if (status < 0)
+       return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well).
+    else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))
+      return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH.
+    else if (flush == MZ_FINISH)
+    {
+       // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH.
+       if (status == TINFL_STATUS_DONE)
+          return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;
+       // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong.
+       else if (!pStream->avail_out)
+          return MZ_BUF_ERROR;
+    }
+    else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))
+      break;
+  }
+
+  return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;
+}
+
+int mz_inflateEnd(mz_streamp pStream)
+{
+  if (!pStream)
+    return MZ_STREAM_ERROR;
+  if (pStream->state)
+  {
+    pStream->zfree(pStream->opaque, pStream->state);
+    pStream->state = NULL;
+  }
+  return MZ_OK;
+}
+
+int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)
+{
+  mz_stream stream;
+  int status;
+  memset(&stream, 0, sizeof(stream));
+
+  // In case mz_ulong is 64-bits (argh I hate longs).
+  if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR;
+
+  stream.next_in = pSource;
+  stream.avail_in = (mz_uint32)source_len;
+  stream.next_out = pDest;
+  stream.avail_out = (mz_uint32)*pDest_len;
+
+  status = mz_inflateInit(&stream);
+  if (status != MZ_OK)
+    return status;
+
+  status = mz_inflate(&stream, MZ_FINISH);
+  if (status != MZ_STREAM_END)
+  {
+    mz_inflateEnd(&stream);
+    return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;
+  }
+  *pDest_len = stream.total_out;
+
+  return mz_inflateEnd(&stream);
+}
+
+const char *mz_error(int err)
+{
+  static struct { int m_err; const char *m_pDesc; } s_error_descs[] =
+  {
+    { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" },
+    { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" }
+  };
+  mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc;
+  return NULL;
+}
+
+#endif //MINIZ_NO_ZLIB_APIS
+
+// ------------------- Low-level Decompression (completely independent from all compression API's)
+
+#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
+#define TINFL_MEMSET(p, c, l) memset(p, c, l)
+
+#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
+#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
+#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
+#define TINFL_CR_FINISH }
+
+// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
+// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
+#define TINFL_GET_BYTE(state_index, c) do { \
+  if (pIn_buf_cur >= pIn_buf_end) { \
+    for ( ; ; ) { \
+      if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
+        TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
+        if (pIn_buf_cur < pIn_buf_end) { \
+          c = *pIn_buf_cur++; \
+          break; \
+        } \
+      } else { \
+        c = 0; \
+        break; \
+      } \
+    } \
+  } else c = *pIn_buf_cur++; } MZ_MACRO_END
+
+#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
+#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
+#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
+
+// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
+// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
+// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
+// bit buffer contains >=15 bits (deflate's max. Huffman code size).
+#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
+  do { \
+    temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
+    if (temp >= 0) { \
+      code_len = temp >> 9; \
+      if ((code_len) && (num_bits >= code_len)) \
+      break; \
+    } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
+       code_len = TINFL_FAST_LOOKUP_BITS; \
+       do { \
+          temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
+       } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
+    } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
+  } while (num_bits < 15);
+
+// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
+// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
+// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
+// The slow path is only executed at the very end of the input buffer.
+#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
+  int temp; mz_uint code_len, c; \
+  if (num_bits < 15) { \
+    if ((pIn_buf_end - pIn_buf_cur) < 2) { \
+       TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
+    } else { \
+       bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
+    } \
+  } \
+  if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
+    code_len = temp >> 9, temp &= 511; \
+  else { \
+    code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
+  } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
+
+tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
+{
+  static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
+  static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
+  static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
+  static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+  static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
+  static const int s_min_table_sizes[3] = { 257, 1, 4 };
+
+  tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
+  const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
+  mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
+  size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
+
+  // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
+  if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
+
+  num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
+  TINFL_CR_BEGIN
+
+  bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
+  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+  {
+    TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
+    counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
+    if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
+    if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
+  }
+
+  do
+  {
+    TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
+    if (r->m_type == 0)
+    {
+      TINFL_SKIP_BITS(5, num_bits & 7);
+      for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
+      if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
+      while ((counter) && (num_bits))
+      {
+        TINFL_GET_BITS(51, dist, 8);
+        while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
+        *pOut_buf_cur++ = (mz_uint8)dist;
+        counter--;
+      }
+      while (counter)
+      {
+        size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
+        while (pIn_buf_cur >= pIn_buf_end)
+        {
+          if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
+          {
+            TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
+          }
+          else
+          {
+            TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
+          }
+        }
+        n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
+        TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
+      }
+    }
+    else if (r->m_type == 3)
+    {
+      TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
+    }
+    else
+    {
+      if (r->m_type == 1)
+      {
+        mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
+        r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
+        for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
+      }
+      else
+      {
+        for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
+        MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
+        r->m_table_sizes[2] = 19;
+      }
+      for ( ; (int)r->m_type >= 0; r->m_type--)
+      {
+        int tree_next, tree_cur; tinfl_huff_table *pTable;
+        mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
+        for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
+        used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
+        for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
+        if ((65536 != total) && (used_syms > 1))
+        {
+          TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
+        }
+        for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
+        {
+          mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
+          cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
+          if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
+          if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
+          rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
+          for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
+          {
+            tree_cur -= ((rev_code >>= 1) & 1);
+            if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
+          }
+          tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
+        }
+        if (r->m_type == 2)
+        {
+          for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
+          {
+            mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
+            if ((dist == 16) && (!counter))
+            {
+              TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
+            }
+            num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
+            TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
+          }
+          if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
+          {
+            TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
+          }
+          TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
+        }
+      }
+      for ( ; ; )
+      {
+        mz_uint8 *pSrc;
+        for ( ; ; )
+        {
+          if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
+          {
+            TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
+            if (counter >= 256)
+              break;
+            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
+            *pOut_buf_cur++ = (mz_uint8)counter;
+          }
+          else
+          {
+            int sym2; mz_uint code_len;
+#if TINFL_USE_64BIT_BITBUF
+            if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
+#else
+            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
+#endif
+            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+              code_len = sym2 >> 9;
+            else
+            {
+              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
+            }
+            counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
+            if (counter & 256)
+              break;
+
+#if !TINFL_USE_64BIT_BITBUF
+            if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
+#endif
+            if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
+              code_len = sym2 >> 9;
+            else
+            {
+              code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
+            }
+            bit_buf >>= code_len; num_bits -= code_len;
+
+            pOut_buf_cur[0] = (mz_uint8)counter;
+            if (sym2 & 256)
+            {
+              pOut_buf_cur++;
+              counter = sym2;
+              break;
+            }
+            pOut_buf_cur[1] = (mz_uint8)sym2;
+            pOut_buf_cur += 2;
+          }
+        }
+        if ((counter &= 511) == 256) break;
+
+        num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
+        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
+
+        TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
+        num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
+        if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
+
+        dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
+        if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
+        {
+          TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
+        }
+
+        pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
+
+        if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
+        {
+          while (counter--)
+          {
+            while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
+            *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
+          }
+          continue;
+        }
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+        else if ((counter >= 9) && (counter <= dist))
+        {
+          const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
+          do
+          {
+            ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
+            ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
+            pOut_buf_cur += 8;
+          } while ((pSrc += 8) < pSrc_end);
+          if ((counter &= 7) < 3)
+          {
+            if (counter)
+            {
+              pOut_buf_cur[0] = pSrc[0];
+              if (counter > 1)
+                pOut_buf_cur[1] = pSrc[1];
+              pOut_buf_cur += counter;
+            }
+            continue;
+          }
+        }
+#endif
+        do
+        {
+          pOut_buf_cur[0] = pSrc[0];
+          pOut_buf_cur[1] = pSrc[1];
+          pOut_buf_cur[2] = pSrc[2];
+          pOut_buf_cur += 3; pSrc += 3;
+        } while ((int)(counter -= 3) > 2);
+        if ((int)counter > 0)
+        {
+          pOut_buf_cur[0] = pSrc[0];
+          if ((int)counter > 1)
+            pOut_buf_cur[1] = pSrc[1];
+          pOut_buf_cur += counter;
+        }
+      }
+    }
+  } while (!(r->m_final & 1));
+  if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
+  {
+    TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
+  }
+  TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
+  TINFL_CR_FINISH
+
+common_exit:
+  r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
+  *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
+  if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
+  {
+    const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
+    mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
+    while (buf_len)
+    {
+      for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
+      {
+        s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
+        s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
+      }
+      for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
+      s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
+    }
+    r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
+  }
+  return status;
+}
+
+// Higher level helper functions.
+void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+  tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
+  *pOut_len = 0;
+  tinfl_init(&decomp);
+  for ( ; ; )
+  {
+    size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
+    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
+      (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+    if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
+    {
+      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
+    }
+    src_buf_ofs += src_buf_size;
+    *pOut_len += dst_buf_size;
+    if (status == TINFL_STATUS_DONE) break;
+    new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
+    pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
+    if (!pNew_buf)
+    {
+      MZ_FREE(pBuf); *pOut_len = 0; return NULL;
+    }
+    pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
+  }
+  return pBuf;
+}
+
+size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+  tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
+  status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
+  return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
+}
+
+int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+  int result = 0;
+  tinfl_decompressor decomp;
+  mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
+  if (!pDict)
+    return TINFL_STATUS_FAILED;
+  tinfl_init(&decomp);
+  for ( ; ; )
+  {
+    size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
+    tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
+      (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
+    in_buf_ofs += in_buf_size;
+    if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
+      break;
+    if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
+    {
+      result = (status == TINFL_STATUS_DONE);
+      break;
+    }
+    dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
+  }
+  MZ_FREE(pDict);
+  *pIn_buf_size = in_buf_ofs;
+  return result;
+}
+
+// ------------------- Low-level Compression (independent from all decompression API's)
+
+// Purposely making these tables static for faster init and thread safety.
+static const mz_uint16 s_tdefl_len_sym[256] = {
+  257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272,
+  273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276,
+  277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,
+  279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
+  281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,
+  282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,
+  283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,
+  284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 };
+
+static const mz_uint8 s_tdefl_len_extra[256] = {
+  0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 };
+
+static const mz_uint8 s_tdefl_small_dist_sym[512] = {
+  0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,
+  11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,
+  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14,
+  14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
+  14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
+  15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16,
+  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+  16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+  17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 };
+
+static const mz_uint8 s_tdefl_small_dist_extra[512] = {
+  0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,
+  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7 };
+
+static const mz_uint8 s_tdefl_large_dist_sym[128] = {
+  0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26,
+  26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,
+  28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 };
+
+static const mz_uint8 s_tdefl_large_dist_extra[128] = {
+  0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,
+  12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
+  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 };
+
+// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values.
+typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq;
+static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1)
+{
+  mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist);
+  for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; }
+  while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
+  for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
+  {
+    const mz_uint32* pHist = &hist[pass << 8];
+    mz_uint offsets[256], cur_ofs = 0;
+    for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
+    for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
+    { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; }
+  }
+  return pCur_syms;
+}
+
+// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair at cs.mu.oz.au, Jyrki Katajainen, jyrki at diku.dk, November 1996.
+static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)
+{
+  int root, leaf, next, avbl, used, dpth;
+  if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
+  A[0].m_key += A[1].m_key; root = 0; leaf = 2;
+  for (next=1; next < n-1; next++)
+  {
+    if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key;
+    if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);
+  }
+  A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
+  avbl = 1; used = dpth = 0; root = n-2; next = n-1;
+  while (avbl>0)
+  {
+    while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
+    while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; }
+    avbl = 2*used; dpth++; used = 0;
+  }
+}
+
+// Limits canonical Huffman code table's max code size.
+enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 };
+static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
+{
+  int i; mz_uint32 total = 0; if (code_list_len <= 1) return;
+  for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
+  for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));
+  while (total != (1UL << max_code_size))
+  {
+    pNum_codes[max_code_size]--;
+    for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
+    total--;
+  }
+}
+
+static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)
+{
+  int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes);
+  if (static_table)
+  {
+    for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++;
+  }
+  else
+  {
+    tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;
+    int num_used_syms = 0;
+    const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];
+    for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; }
+
+    pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);
+
+    for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++;
+
+    tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);
+
+    MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);
+    for (i = 1, j = num_used_syms; i <= code_size_limit; i++)
+      for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);
+  }
+
+  next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1);
+
+  for (i = 0; i < table_len; i++)
+  {
+    mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue;
+    code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1);
+    d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;
+  }
+}
+
+#define TDEFL_PUT_BITS(b, l) do { \
+  mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \
+  d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \
+  while (d->m_bits_in >= 8) { \
+    if (d->m_pOutput_buf < d->m_pOutput_buf_end) \
+      *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \
+      d->m_bit_buffer >>= 8; \
+      d->m_bits_in -= 8; \
+  } \
+} MZ_MACRO_END
+
+#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \
+  if (rle_repeat_count < 3) { \
+    d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \
+    while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \
+  } else { \
+    d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \
+} rle_repeat_count = 0; } }
+
+#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \
+  if (rle_z_count < 3) { \
+    d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \
+  } else if (rle_z_count <= 10) { \
+    d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \
+  } else { \
+    d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \
+} rle_z_count = 0; } }
+
+static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+static void tdefl_start_dynamic_block(tdefl_compressor *d)
+{
+  int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;
+  mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;
+
+  d->m_huff_count[0][256] = 1;
+
+  tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);
+  tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);
+
+  for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break;
+  for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break;
+
+  memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);
+  memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);
+  total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0;
+
+  memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);
+  for (i = 0; i < total_code_sizes_to_pack; i++)
+  {
+    mz_uint8 code_size = code_sizes_to_pack[i];
+    if (!code_size)
+    {
+      TDEFL_RLE_PREV_CODE_SIZE();
+      if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); }
+    }
+    else
+    {
+      TDEFL_RLE_ZERO_CODE_SIZE();
+      if (code_size != prev_code_size)
+      {
+        TDEFL_RLE_PREV_CODE_SIZE();
+        d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size;
+      }
+      else if (++rle_repeat_count == 6)
+      {
+        TDEFL_RLE_PREV_CODE_SIZE();
+      }
+    }
+    prev_code_size = code_size;
+  }
+  if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); }
+
+  tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);
+
+  TDEFL_PUT_BITS(2, 2);
+
+  TDEFL_PUT_BITS(num_lit_codes - 257, 5);
+  TDEFL_PUT_BITS(num_dist_codes - 1, 5);
+
+  for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break;
+  num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4);
+  for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);
+
+  for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; )
+  {
+    mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);
+    TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);
+    if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]);
+  }
+}
+
+static void tdefl_start_static_block(tdefl_compressor *d)
+{
+  mz_uint i;
+  mz_uint8 *p = &d->m_huff_code_sizes[0][0];
+
+  for (i = 0; i <= 143; ++i) *p++ = 8;
+  for ( ; i <= 255; ++i) *p++ = 9;
+  for ( ; i <= 279; ++i) *p++ = 7;
+  for ( ; i <= 287; ++i) *p++ = 8;
+
+  memset(d->m_huff_code_sizes[1], 5, 32);
+
+  tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);
+  tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);
+
+  TDEFL_PUT_BITS(1, 2);
+}
+
+static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+  mz_uint flags;
+  mz_uint8 *pLZ_codes;
+  mz_uint8 *pOutput_buf = d->m_pOutput_buf;
+  mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;
+  mz_uint64 bit_buffer = d->m_bit_buffer;
+  mz_uint bits_in = d->m_bits_in;
+
+#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); }
+
+  flags = 1;
+  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)
+  {
+    if (flags == 1)
+      flags = *pLZ_codes++ | 0x100;
+
+    if (flags & 1)
+    {
+      mz_uint s0, s1, n0, n1, sym, num_extra_bits;
+      mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3;
+
+      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+      TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+      // This sequence coaxes MSVC into using cmov's vs. jmp's.
+      s0 = s_tdefl_small_dist_sym[match_dist & 511];
+      n0 = s_tdefl_small_dist_extra[match_dist & 511];
+      s1 = s_tdefl_large_dist_sym[match_dist >> 8];
+      n1 = s_tdefl_large_dist_extra[match_dist >> 8];
+      sym = (match_dist < 512) ? s0 : s1;
+      num_extra_bits = (match_dist < 512) ? n0 : n1;
+
+      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+      TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+      TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+    }
+    else
+    {
+      mz_uint lit = *pLZ_codes++;
+      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+      TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+      if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+      {
+        flags >>= 1;
+        lit = *pLZ_codes++;
+        MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+        TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+
+        if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))
+        {
+          flags >>= 1;
+          lit = *pLZ_codes++;
+          MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+          TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+        }
+      }
+    }
+
+    if (pOutput_buf >= d->m_pOutput_buf_end)
+      return MZ_FALSE;
+
+    *(mz_uint64*)pOutput_buf = bit_buffer;
+    pOutput_buf += (bits_in >> 3);
+    bit_buffer >>= (bits_in & ~7);
+    bits_in &= 7;
+  }
+
+#undef TDEFL_PUT_BITS_FAST
+
+  d->m_pOutput_buf = pOutput_buf;
+  d->m_bits_in = 0;
+  d->m_bit_buffer = 0;
+
+  while (bits_in)
+  {
+    mz_uint32 n = MZ_MIN(bits_in, 16);
+    TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);
+    bit_buffer >>= n;
+    bits_in -= n;
+  }
+
+  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#else
+static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)
+{
+  mz_uint flags;
+  mz_uint8 *pLZ_codes;
+
+  flags = 1;
+  for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)
+  {
+    if (flags == 1)
+      flags = *pLZ_codes++ | 0x100;
+    if (flags & 1)
+    {
+      mz_uint sym, num_extra_bits;
+      mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3;
+
+      MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+      TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);
+      TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);
+
+      if (match_dist < 512)
+      {
+        sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist];
+      }
+      else
+      {
+        sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];
+      }
+      MZ_ASSERT(d->m_huff_code_sizes[1][sym]);
+      TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);
+      TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);
+    }
+    else
+    {
+      mz_uint lit = *pLZ_codes++;
+      MZ_ASSERT(d->m_huff_code_sizes[0][lit]);
+      TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);
+    }
+  }
+
+  TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);
+
+  return (d->m_pOutput_buf < d->m_pOutput_buf_end);
+}
+#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS
+
+static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)
+{
+  if (static_block)
+    tdefl_start_static_block(d);
+  else
+    tdefl_start_dynamic_block(d);
+  return tdefl_compress_lz_codes(d);
+}
+
+static int tdefl_flush_block(tdefl_compressor *d, int flush)
+{
+  mz_uint saved_bit_buf, saved_bits_in;
+  mz_uint8 *pSaved_output_buf;
+  mz_bool comp_block_succeeded = MZ_FALSE;
+  int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;
+  mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;
+
+  d->m_pOutput_buf = pOutput_buf_start;
+  d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;
+
+  MZ_ASSERT(!d->m_output_flush_remaining);
+  d->m_output_flush_ofs = 0;
+  d->m_output_flush_remaining = 0;
+
+  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);
+  d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);
+
+  if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))
+  {
+    TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8);
+  }
+
+  TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);
+
+  pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in;
+
+  if (!use_raw_block)
+    comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));
+
+  // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead.
+  if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&
+       ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) )
+  {
+    mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+    TDEFL_PUT_BITS(0, 2);
+    if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
+    for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)
+    {
+      TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);
+    }
+    for (i = 0; i < d->m_total_lz_bytes; ++i)
+    {
+      TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);
+    }
+  }
+  // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes.
+  else if (!comp_block_succeeded)
+  {
+    d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;
+    tdefl_compress_block(d, MZ_TRUE);
+  }
+
+  if (flush)
+  {
+    if (flush == TDEFL_FINISH)
+    {
+      if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); }
+      if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } }
+    }
+    else
+    {
+      mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); }
+    }
+  }
+
+  MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);
+
+  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+
+  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++;
+
+  if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)
+  {
+    if (d->m_pPut_buf_func)
+    {
+      *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+      if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))
+        return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);
+    }
+    else if (pOutput_buf_start == d->m_output_buf)
+    {
+      int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));
+      memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);
+      d->m_out_buf_ofs += bytes_to_copy;
+      if ((n -= bytes_to_copy) != 0)
+      {
+        d->m_output_flush_ofs = bytes_to_copy;
+        d->m_output_flush_remaining = n;
+      }
+    }
+    else
+    {
+      d->m_out_buf_ofs += n;
+    }
+  }
+
+  return d->m_output_flush_remaining;
+}
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p)
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+  const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q;
+  mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s);
+  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
+  for ( ; ; )
+  {
+    for ( ; ; )
+    {
+      if (--num_probes_left == 0) return;
+      #define TDEFL_PROBE \
+        next_probe_pos = d->m_next[probe_pos]; \
+        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
+        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+        if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break;
+      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
+    }
+    if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32;
+    do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
+                   (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
+    if (!probe_len)
+    {
+      *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break;
+    }
+    else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len)
+    {
+      *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break;
+      c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);
+    }
+  }
+}
+#else
+static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)
+{
+  mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;
+  mz_uint num_probes_left = d->m_max_probes[match_len >= 32];
+  const mz_uint8 *s = d->m_dict + pos, *p, *q;
+  mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];
+  MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return;
+  for ( ; ; )
+  {
+    for ( ; ; )
+    {
+      if (--num_probes_left == 0) return;
+      #define TDEFL_PROBE \
+        next_probe_pos = d->m_next[probe_pos]; \
+        if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \
+        probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \
+        if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break;
+      TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE;
+    }
+    if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break;
+    if (probe_len > match_len)
+    {
+      *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return;
+      c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1];
+    }
+  }
+}
+#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+static mz_bool tdefl_compress_fast(tdefl_compressor *d)
+{
+  // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio.
+  mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;
+  mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;
+  mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+
+  while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))
+  {
+    const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;
+    mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+    mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);
+    d->m_src_buf_left -= num_bytes_to_process;
+    lookahead_size += num_bytes_to_process;
+
+    while (num_bytes_to_process)
+    {
+      mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);
+      memcpy(d->m_dict + dst_pos, d->m_pSrc, n);
+      if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+        memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));
+      d->m_pSrc += n;
+      dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;
+      num_bytes_to_process -= n;
+    }
+
+    dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);
+    if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break;
+
+    while (lookahead_size >= 4)
+    {
+      mz_uint cur_match_dist, cur_match_len = 1;
+      mz_uint8 *pCur_dict = d->m_dict + cur_pos;
+      mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF;
+      mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;
+      mz_uint probe_pos = d->m_hash[hash];
+      d->m_hash[hash] = (mz_uint16)lookahead_pos;
+
+      if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))
+      {
+        const mz_uint16 *p = (const mz_uint16 *)pCur_dict;
+        const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);
+        mz_uint32 probe_len = 32;
+        do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) &&
+          (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) );
+        cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);
+        if (!probe_len)
+          cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;
+
+        if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)))
+        {
+          cur_match_len = 1;
+          *pLZ_code_buf++ = (mz_uint8)first_trigram;
+          *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+          d->m_huff_count[0][(mz_uint8)first_trigram]++;
+        }
+        else
+        {
+          mz_uint32 s0, s1;
+          cur_match_len = MZ_MIN(cur_match_len, lookahead_size);
+
+          MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));
+
+          cur_match_dist--;
+
+          pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);
+          *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;
+          pLZ_code_buf += 3;
+          *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);
+
+          s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];
+          s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];
+          d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;
+
+          d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;
+        }
+      }
+      else
+      {
+        *pLZ_code_buf++ = (mz_uint8)first_trigram;
+        *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+        d->m_huff_count[0][(mz_uint8)first_trigram]++;
+      }
+
+      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
+
+      total_lz_bytes += cur_match_len;
+      lookahead_pos += cur_match_len;
+      dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE);
+      cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;
+      MZ_ASSERT(lookahead_size >= cur_match_len);
+      lookahead_size -= cur_match_len;
+
+      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+      {
+        int n;
+        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
+        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
+        if ((n = tdefl_flush_block(d, 0)) != 0)
+          return (n < 0) ? MZ_FALSE : MZ_TRUE;
+        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
+      }
+    }
+
+    while (lookahead_size)
+    {
+      mz_uint8 lit = d->m_dict[cur_pos];
+
+      total_lz_bytes++;
+      *pLZ_code_buf++ = lit;
+      *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);
+      if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; }
+
+      d->m_huff_count[0][lit]++;
+
+      lookahead_pos++;
+      dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE);
+      cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;
+      lookahead_size--;
+
+      if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])
+      {
+        int n;
+        d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
+        d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
+        if ((n = tdefl_flush_block(d, 0)) != 0)
+          return (n < 0) ? MZ_FALSE : MZ_TRUE;
+        total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left;
+      }
+    }
+  }
+
+  d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size;
+  d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left;
+  return MZ_TRUE;
+}
+#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+
+static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)
+{
+  d->m_total_lz_bytes++;
+  *d->m_pLZ_code_buf++ = lit;
+  *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
+  d->m_huff_count[0][lit]++;
+}
+
+static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)
+{
+  mz_uint32 s0, s1;
+
+  MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));
+
+  d->m_total_lz_bytes += match_len;
+
+  d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);
+
+  match_dist -= 1;
+  d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);
+  d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3;
+
+  *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; }
+
+  s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];
+  d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;
+
+  if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;
+}
+
+static mz_bool tdefl_compress_normal(tdefl_compressor *d)
+{
+  const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left;
+  tdefl_flush flush = d->m_flush;
+
+  while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))
+  {
+    mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;
+    // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN.
+    if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))
+    {
+      mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;
+      mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];
+      mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);
+      const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;
+      src_buf_left -= num_bytes_to_process;
+      d->m_lookahead_size += num_bytes_to_process;
+      while (pSrc != pSrc_end)
+      {
+        mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+        hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+        d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
+        dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++;
+      }
+    }
+    else
+    {
+      while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+      {
+        mz_uint8 c = *pSrc++;
+        mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;
+        src_buf_left--;
+        d->m_dict[dst_pos] = c;
+        if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))
+          d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;
+        if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)
+        {
+          mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;
+          mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);
+          d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos);
+        }
+      }
+    }
+    d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);
+    if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))
+      break;
+
+    // Simple lazy/greedy parsing state machine.
+    len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;
+    if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))
+    {
+      if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))
+      {
+        mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];
+        cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; }
+        if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1;
+      }
+    }
+    else
+    {
+      tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);
+    }
+    if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))
+    {
+      cur_match_dist = cur_match_len = 0;
+    }
+    if (d->m_saved_match_len)
+    {
+      if (cur_match_len > d->m_saved_match_len)
+      {
+        tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);
+        if (cur_match_len >= 128)
+        {
+          tdefl_record_match(d, cur_match_len, cur_match_dist);
+          d->m_saved_match_len = 0; len_to_move = cur_match_len;
+        }
+        else
+        {
+          d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
+        }
+      }
+      else
+      {
+        tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);
+        len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0;
+      }
+    }
+    else if (!cur_match_dist)
+      tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);
+    else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))
+    {
+      tdefl_record_match(d, cur_match_len, cur_match_dist);
+      len_to_move = cur_match_len;
+    }
+    else
+    {
+      d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len;
+    }
+    // Move the lookahead forward by len_to_move bytes.
+    d->m_lookahead_pos += len_to_move;
+    MZ_ASSERT(d->m_lookahead_size >= len_to_move);
+    d->m_lookahead_size -= len_to_move;
+    d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE);
+    // Check if it's time to flush the current LZ codes to the internal output buffer.
+    if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||
+         ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) )
+    {
+      int n;
+      d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
+      if ((n = tdefl_flush_block(d, 0)) != 0)
+        return (n < 0) ? MZ_FALSE : MZ_TRUE;
+    }
+  }
+
+  d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left;
+  return MZ_TRUE;
+}
+
+static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)
+{
+  if (d->m_pIn_buf_size)
+  {
+    *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;
+  }
+
+  if (d->m_pOut_buf_size)
+  {
+    size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);
+    memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);
+    d->m_output_flush_ofs += (mz_uint)n;
+    d->m_output_flush_remaining -= (mz_uint)n;
+    d->m_out_buf_ofs += n;
+
+    *d->m_pOut_buf_size = d->m_out_buf_ofs;
+  }
+
+  return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)
+{
+  if (!d)
+  {
+    if (pIn_buf_size) *pIn_buf_size = 0;
+    if (pOut_buf_size) *pOut_buf_size = 0;
+    return TDEFL_STATUS_BAD_PARAM;
+  }
+
+  d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size;
+  d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size;
+  d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;
+  d->m_out_buf_ofs = 0;
+  d->m_flush = flush;
+
+  if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||
+        (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) )
+  {
+    if (pIn_buf_size) *pIn_buf_size = 0;
+    if (pOut_buf_size) *pOut_buf_size = 0;
+    return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);
+  }
+  d->m_wants_to_finish |= (flush == TDEFL_FINISH);
+
+  if ((d->m_output_flush_remaining) || (d->m_finished))
+    return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+
+#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+  if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&
+      ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&
+      ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))
+  {
+    if (!tdefl_compress_fast(d))
+      return d->m_prev_return_status;
+  }
+  else
+#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
+  {
+    if (!tdefl_compress_normal(d))
+      return d->m_prev_return_status;
+  }
+
+  if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))
+    d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);
+
+  if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))
+  {
+    if (tdefl_flush_block(d, flush) < 0)
+      return d->m_prev_return_status;
+    d->m_finished = (flush == TDEFL_FINISH);
+    if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; }
+  }
+
+  return (d->m_prev_return_status = tdefl_flush_output_buffer(d));
+}
+
+tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)
+{
+  MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);
+}
+
+tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+  d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user;
+  d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;
+  d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;
+  if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash);
+  d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;
+  d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;
+  d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8;
+  d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY;
+  d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1;
+  d->m_pIn_buf = NULL; d->m_pOut_buf = NULL;
+  d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL;
+  d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0;
+  memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);
+  memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);
+  return TDEFL_STATUS_OKAY;
+}
+
+tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)
+{
+  return d->m_prev_return_status;
+}
+
+mz_uint32 tdefl_get_adler32(tdefl_compressor *d)
+{
+  return d->m_adler32;
+}
+
+mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
+{
+  tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE;
+  pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE;
+  succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);
+  succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);
+  MZ_FREE(pComp); return succeeded;
+}
+
+typedef struct
+{
+  size_t m_size, m_capacity;
+  mz_uint8 *m_pBuf;
+  mz_bool m_expandable;
+} tdefl_output_buffer;
+
+static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)
+{
+  tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;
+  size_t new_size = p->m_size + len;
+  if (new_size > p->m_capacity)
+  {
+    size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE;
+    do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity);
+    pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE;
+    p->m_pBuf = pNew_buf; p->m_capacity = new_capacity;
+  }
+  memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size;
+  return MZ_TRUE;
+}
+
+void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
+{
+  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
+  if (!pOut_len) return MZ_FALSE; else *pOut_len = 0;
+  out_buf.m_expandable = MZ_TRUE;
+  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL;
+  *pOut_len = out_buf.m_size; return out_buf.m_pBuf;
+}
+
+size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
+{
+  tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf);
+  if (!pOut_buf) return 0;
+  out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len;
+  if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0;
+  return out_buf.m_size;
+}
+
+#ifndef MINIZ_NO_ZLIB_APIS
+static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32,  16, 32, 128, 256,  512, 768, 1500 };
+
+// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files).
+mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)
+{
+  mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+  if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER;
+
+  if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+  else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES;
+  else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK;
+  else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;
+  else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES;
+
+  return comp_flags;
+}
+#endif //MINIZ_NO_ZLIB_APIS
+
+#ifdef _MSC_VER
+#pragma warning (push)
+#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal)
+#endif
+
+// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at
+// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.
+// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck.
+void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)
+{
+  // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined.
+  static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32,  16, 32, 128, 256,  512, 768, 1500 };
+  tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0;
+  if (!pComp) return NULL;
+  MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; }
+  // write dummy header
+  for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf);
+  // compress image data
+  tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);
+  for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); }
+  if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
+  // write real header
+  *pLen_out = out_buf.m_size-41;
+  {
+    static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06};
+    mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
+      0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0,
+      (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54};
+    c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24);
+    memcpy(out_buf.m_pBuf, pnghdr, 41);
+  }
+  // write footer (IDAT CRC-32, followed by IEND chunk)
+  if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; }
+  c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24);
+  // compute final size of file, grab compressed data buffer and return
+  *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf;
+}
+void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)
+{
+  // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out)
+  return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);
+}
+
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
+
+// ------------------- .ZIP archive reading
+
+#ifndef MINIZ_NO_ARCHIVE_APIS
+
+#ifdef MINIZ_NO_STDIO
+  #define MZ_FILE void *
+#else
+  #include <stdio.h>
+  #include <sys/stat.h>
+
+  #if defined(_MSC_VER) || defined(__MINGW64__)
+    static FILE *mz_fopen(const char *pFilename, const char *pMode)
+    {
+      FILE* pFile = NULL;
+      fopen_s(&pFile, pFilename, pMode);
+      return pFile;
+    }
+    static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
+    {
+      FILE* pFile = NULL;
+      if (freopen_s(&pFile, pPath, pMode, pStream))
+        return NULL;
+      return pFile;
+    }
+    #ifndef MINIZ_NO_TIME
+      #include <sys/utime.h>
+    #endif
+    #define MZ_FILE FILE
+    #define MZ_FOPEN mz_fopen
+    #define MZ_FCLOSE fclose
+    #define MZ_FREAD fread
+    #define MZ_FWRITE fwrite
+    #define MZ_FTELL64 _ftelli64
+    #define MZ_FSEEK64 _fseeki64
+    #define MZ_FILE_STAT_STRUCT _stat
+    #define MZ_FILE_STAT _stat
+    #define MZ_FFLUSH fflush
+    #define MZ_FREOPEN mz_freopen
+    #define MZ_DELETE_FILE remove
+  #elif defined(__MINGW32__)
+    #ifndef MINIZ_NO_TIME
+      #include <sys/utime.h>
+    #endif
+    #define MZ_FILE FILE
+    #define MZ_FOPEN(f, m) fopen(f, m)
+    #define MZ_FCLOSE fclose
+    #define MZ_FREAD fread
+    #define MZ_FWRITE fwrite
+    #define MZ_FTELL64 ftello64
+    #define MZ_FSEEK64 fseeko64
+    #define MZ_FILE_STAT_STRUCT _stat
+    #define MZ_FILE_STAT _stat
+    #define MZ_FFLUSH fflush
+    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+    #define MZ_DELETE_FILE remove
+  #elif defined(__TINYC__)
+    #ifndef MINIZ_NO_TIME
+      #include <sys/utime.h>
+    #endif
+    #define MZ_FILE FILE
+    #define MZ_FOPEN(f, m) fopen(f, m)
+    #define MZ_FCLOSE fclose
+    #define MZ_FREAD fread
+    #define MZ_FWRITE fwrite
+    #define MZ_FTELL64 ftell
+    #define MZ_FSEEK64 fseek
+    #define MZ_FILE_STAT_STRUCT stat
+    #define MZ_FILE_STAT stat
+    #define MZ_FFLUSH fflush
+    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+    #define MZ_DELETE_FILE remove
+  #elif defined(__GNUC__) && _LARGEFILE64_SOURCE
+    #ifndef MINIZ_NO_TIME
+      #include <utime.h>
+    #endif
+    #define MZ_FILE FILE
+    #define MZ_FOPEN(f, m) fopen64(f, m)
+    #define MZ_FCLOSE fclose
+    #define MZ_FREAD fread
+    #define MZ_FWRITE fwrite
+    #define MZ_FTELL64 ftello64
+    #define MZ_FSEEK64 fseeko64
+    #define MZ_FILE_STAT_STRUCT stat64
+    #define MZ_FILE_STAT stat64
+    #define MZ_FFLUSH fflush
+    #define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
+    #define MZ_DELETE_FILE remove
+  #else
+    #ifndef MINIZ_NO_TIME
+      #include <utime.h>
+    #endif
+    #define MZ_FILE FILE
+    #define MZ_FOPEN(f, m) fopen(f, m)
+    #define MZ_FCLOSE fclose
+    #define MZ_FREAD fread
+    #define MZ_FWRITE fwrite
+    #define MZ_FTELL64 ftello
+    #define MZ_FSEEK64 fseeko
+    #define MZ_FILE_STAT_STRUCT stat
+    #define MZ_FILE_STAT stat
+    #define MZ_FFLUSH fflush
+    #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
+    #define MZ_DELETE_FILE remove
+  #endif // #ifdef _MSC_VER
+#endif // #ifdef MINIZ_NO_STDIO
+
+#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
+
+// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
+enum
+{
+  // ZIP archive identifiers and record sizes
+  MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
+  MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
+  // Central directory header record offsets
+  MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
+  MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16,
+  MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
+  MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
+  // Local directory header offsets
+  MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10,
+  MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
+  MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
+  // End of central directory offsets
+  MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
+  MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
+};
+
+typedef struct
+{
+  void *m_p;
+  size_t m_size, m_capacity;
+  mz_uint m_element_size;
+} mz_zip_array;
+
+struct mz_zip_internal_state_tag
+{
+  mz_zip_array m_central_dir;
+  mz_zip_array m_central_dir_offsets;
+  mz_zip_array m_sorted_central_dir_offsets;
+  MZ_FILE *m_pFile;
+  void *m_pMem;
+  size_t m_mem_size;
+  size_t m_mem_capacity;
+};
+
+#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
+#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
+
+static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
+{
+  pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
+  memset(pArray, 0, sizeof(mz_zip_array));
+}
+
+static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
+{
+  void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE;
+  if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; }
+  if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE;
+  pArray->m_p = pNew_p; pArray->m_capacity = new_capacity;
+  return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
+{
+  if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; }
+  return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
+{
+  if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; }
+  pArray->m_size = new_size;
+  return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
+{
+  return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
+{
+  size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE;
+  memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_TIME
+static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date)
+{
+  struct tm tm;
+  memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1;
+  tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31;
+  tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62;
+  return mktime(&tm);
+}
+
+static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
+{
+#ifdef _MSC_VER
+  struct tm tm_struct;
+  struct tm *tm = &tm_struct;
+  errno_t err = localtime_s(tm, &time);
+  if (err)
+  {
+    *pDOS_date = 0; *pDOS_time = 0;
+    return;
+  }
+#else
+  struct tm *tm = localtime(&time);
+#endif
+  *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
+  *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
+}
+#endif
+
+#ifndef MINIZ_NO_STDIO
+static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
+{
+#ifdef MINIZ_NO_TIME
+  (void)pFilename; *pDOS_date = *pDOS_time = 0;
+#else
+  struct MZ_FILE_STAT_STRUCT file_stat;
+  // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
+  if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
+    return MZ_FALSE;
+  mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
+#endif // #ifdef MINIZ_NO_TIME
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_TIME
+static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time)
+{
+  struct utimbuf t; t.actime = access_time; t.modtime = modified_time;
+  return !utime(pFilename, &t);
+}
+#endif // #ifndef MINIZ_NO_TIME
+#endif // #ifndef MINIZ_NO_STDIO
+
+static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags)
+{
+  (void)flags;
+  if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
+    return MZ_FALSE;
+
+  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
+  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
+  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
+
+  pZip->m_zip_mode = MZ_ZIP_MODE_READING;
+  pZip->m_archive_size = 0;
+  pZip->m_central_directory_file_ofs = 0;
+  pZip->m_total_files = 0;
+
+  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
+    return MZ_FALSE;
+  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
+  return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
+{
+  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
+  const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
+  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+  mz_uint8 l = 0, r = 0;
+  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+  pE = pL + MZ_MIN(l_len, r_len);
+  while (pL < pE)
+  {
+    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
+      break;
+    pL++; pR++;
+  }
+  return (pL == pE) ? (l_len < r_len) : (l < r);
+}
+
+#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END
+
+// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.)
+static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
+{
+  mz_zip_internal_state *pState = pZip->m_pState;
+  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
+  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
+  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
+  const int size = pZip->m_total_files;
+  int start = (size - 2) >> 1, end;
+  while (start >= 0)
+  {
+    int child, root = start;
+    for ( ; ; )
+    {
+      if ((child = (root << 1) + 1) >= size)
+        break;
+      child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])));
+      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
+        break;
+      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
+    }
+    start--;
+  }
+
+  end = size - 1;
+  while (end > 0)
+  {
+    int child, root = 0;
+    MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
+    for ( ; ; )
+    {
+      if ((child = (root << 1) + 1) >= end)
+        break;
+      child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]));
+      if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
+        break;
+      MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child;
+    }
+    end--;
+  }
+}
+
+static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags)
+{
+  mz_uint cdir_size, num_this_disk, cdir_disk_index;
+  mz_uint64 cdir_ofs;
+  mz_int64 cur_file_ofs;
+  const mz_uint8 *p;
+  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
+  mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
+  // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
+  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  // Find the end of central directory record by scanning the file from the end towards the beginning.
+  cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
+  for ( ; ; )
+  {
+    int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
+    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
+      return MZ_FALSE;
+    for (i = n - 4; i >= 0; --i)
+      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
+        break;
+    if (i >= 0)
+    {
+      cur_file_ofs += i;
+      break;
+    }
+    if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
+      return MZ_FALSE;
+    cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
+  }
+  // Read and verify the end of central directory record.
+  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
+      ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
+    return MZ_FALSE;
+
+  num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
+  cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
+  if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
+    return MZ_FALSE;
+
+  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+
+  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
+  if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
+    return MZ_FALSE;
+
+  pZip->m_central_directory_file_ofs = cdir_ofs;
+
+  if (pZip->m_total_files)
+  {
+     mz_uint i, n;
+
+    // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices.
+    if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
+        (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
+      return MZ_FALSE;
+
+    if (sort_central_dir)
+    {
+      if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
+        return MZ_FALSE;
+    }
+
+    if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
+      return MZ_FALSE;
+
+    // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported).
+    p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
+    for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
+    {
+      mz_uint total_header_size, comp_size, decomp_size, disk_index;
+      if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
+        return MZ_FALSE;
+      MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
+      if (sort_central_dir)
+        MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
+      comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+      decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
+      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF))
+        return MZ_FALSE;
+      disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
+      if ((disk_index != num_this_disk) && (disk_index != 1))
+        return MZ_FALSE;
+      if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
+        return MZ_FALSE;
+      if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
+        return MZ_FALSE;
+      n -= total_header_size; p += total_header_size;
+    }
+  }
+
+  if (sort_central_dir)
+    mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
+
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags)
+{
+  if ((!pZip) || (!pZip->m_pRead))
+    return MZ_FALSE;
+  if (!mz_zip_reader_init_internal(pZip, flags))
+    return MZ_FALSE;
+  pZip->m_archive_size = size;
+  if (!mz_zip_reader_read_central_dir(pZip, flags))
+  {
+    mz_zip_reader_end(pZip);
+    return MZ_FALSE;
+  }
+  return MZ_TRUE;
+}
+
+static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+  size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
+  memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
+  return s;
+}
+
+mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags)
+{
+  if (!mz_zip_reader_init_internal(pZip, flags))
+    return MZ_FALSE;
+  pZip->m_archive_size = size;
+  pZip->m_pRead = mz_zip_mem_read_func;
+  pZip->m_pIO_opaque = pZip;
+#ifdef __cplusplus
+  pZip->m_pState->m_pMem = const_cast<void *>(pMem);
+#else
+  pZip->m_pState->m_pMem = (void *)pMem;
+#endif
+  pZip->m_pState->m_mem_size = size;
+  if (!mz_zip_reader_read_central_dir(pZip, flags))
+  {
+    mz_zip_reader_end(pZip);
+    return MZ_FALSE;
+  }
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
+{
+  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
+  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
+    return 0;
+  return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
+}
+
+mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)
+{
+  mz_uint64 file_size;
+  MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
+  if (!pFile)
+    return MZ_FALSE;
+  if (MZ_FSEEK64(pFile, 0, SEEK_END))
+  {
+    MZ_FCLOSE(pFile);
+    return MZ_FALSE;
+  }
+  file_size = MZ_FTELL64(pFile);
+  if (!mz_zip_reader_init_internal(pZip, flags))
+  {
+    MZ_FCLOSE(pFile);
+    return MZ_FALSE;
+  }
+  pZip->m_pRead = mz_zip_file_read_func;
+  pZip->m_pIO_opaque = pZip;
+  pZip->m_pState->m_pFile = pFile;
+  pZip->m_archive_size = file_size;
+  if (!mz_zip_reader_read_central_dir(pZip, flags))
+  {
+    mz_zip_reader_end(pZip);
+    return MZ_FALSE;
+  }
+  return MZ_TRUE;
+}
+#endif // #ifndef MINIZ_NO_STDIO
+
+mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)
+{
+  return pZip ? pZip->m_total_files : 0;
+}
+
+static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
+{
+  if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+    return NULL;
+  return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
+}
+
+mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
+{
+  mz_uint m_bit_flag;
+  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
+  if (!p)
+    return MZ_FALSE;
+  m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+  return (m_bit_flag & 1);
+}
+
+mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
+{
+  mz_uint filename_len, external_attr;
+  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
+  if (!p)
+    return MZ_FALSE;
+
+  // First see if the filename ends with a '/' character.
+  filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+  if (filename_len)
+  {
+    if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
+      return MZ_TRUE;
+  }
+
+  // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
+  // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
+  // FIXME: Remove this check? Is it necessary - we already check the filename.
+  external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
+  if ((external_attr & 0x10) != 0)
+    return MZ_TRUE;
+
+  return MZ_FALSE;
+}
+
+mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
+{
+  mz_uint n;
+  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
+  if ((!p) || (!pStat))
+    return MZ_FALSE;
+
+  // Unpack the central directory record.
+  pStat->m_file_index = file_index;
+  pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
+  pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
+  pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
+  pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+  pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
+#ifndef MINIZ_NO_TIME
+  pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
+#endif
+  pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
+  pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+  pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
+  pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
+  pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
+  pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
+
+  // Copy as much of the filename and comment as possible.
+  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
+  memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0';
+
+  n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
+  pStat->m_comment_size = n;
+  memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0';
+
+  return MZ_TRUE;
+}
+
+mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
+{
+  mz_uint n;
+  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
+  if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; }
+  n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+  if (filename_buf_size)
+  {
+    n = MZ_MIN(n, filename_buf_size - 1);
+    memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
+    pFilename[n] = '\0';
+  }
+  return n + 1;
+}
+
+static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
+{
+  mz_uint i;
+  if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
+    return 0 == memcmp(pA, pB, len);
+  for (i = 0; i < len; ++i)
+    if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
+      return MZ_FALSE;
+  return MZ_TRUE;
+}
+
+static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
+{
+  const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
+  mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+  mz_uint8 l = 0, r = 0;
+  pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+  pE = pL + MZ_MIN(l_len, r_len);
+  while (pL < pE)
+  {
+    if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
+      break;
+    pL++; pR++;
+  }
+  return (pL == pE) ? (int)(l_len - r_len) : (l - r);
+}
+
+static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename)
+{
+  mz_zip_internal_state *pState = pZip->m_pState;
+  const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
+  const mz_zip_array *pCentral_dir = &pState->m_central_dir;
+  mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
+  const int size = pZip->m_total_files;
+  const mz_uint filename_len = (mz_uint)strlen(pFilename);
+  int l = 0, h = size - 1;
+  while (l <= h)
+  {
+    int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
+    if (!comp)
+      return file_index;
+    else if (comp < 0)
+      l = m + 1;
+    else
+      h = m - 1;
+  }
+  return -1;
+}
+
+int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)
+{
+  mz_uint file_index; size_t name_len, comment_len;
+  if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+    return -1;
+  if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
+    return mz_zip_reader_locate_file_binary_search(pZip, pName);
+  name_len = strlen(pName); if (name_len > 0xFFFF) return -1;
+  comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1;
+  for (file_index = 0; file_index < pZip->m_total_files; file_index++)
+  {
+    const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
+    mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+    const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+    if (filename_len < name_len)
+      continue;
+    if (comment_len)
+    {
+      mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
+      const char *pFile_comment = pFilename + filename_len + file_extra_len;
+      if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags)))
+        continue;
+    }
+    if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
+    {
+      int ofs = filename_len - 1;
+      do
+      {
+        if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
+          break;
+      } while (--ofs >= 0);
+      ofs++;
+      pFilename += ofs; filename_len -= ofs;
+    }
+    if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags)))
+      return file_index;
+  }
+  return -1;
+}
+
+mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
+{
+  int status = TINFL_STATUS_DONE;
+  mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
+  mz_zip_archive_file_stat file_stat;
+  void *pRead_buf;
+  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+  tinfl_decompressor inflator;
+
+  if ((buf_size) && (!pBuf))
+    return MZ_FALSE;
+
+  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+    return MZ_FALSE;
+
+  // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
+  if (!file_stat.m_comp_size)
+    return MZ_TRUE;
+
+  // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
+  // I'm torn how to handle this case - should it fail instead?
+  if (mz_zip_reader_is_file_a_directory(pZip, file_index))
+    return MZ_TRUE;
+
+  // Encryption and patch files are not supported.
+  if (file_stat.m_bit_flag & (1 | 32))
+    return MZ_FALSE;
+
+  // This function only supports stored and deflate.
+  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
+    return MZ_FALSE;
+
+  // Ensure supplied output buffer is large enough.
+  needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
+  if (buf_size < needed_size)
+    return MZ_FALSE;
+
+  // Read and parse the local directory entry.
+  cur_file_ofs = file_stat.m_local_header_ofs;
+  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+    return MZ_FALSE;
+
+  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
+    return MZ_FALSE;
+
+  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
+  {
+    // The file is stored or the caller has requested the compressed data.
+    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
+      return MZ_FALSE;
+    return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32);
+  }
+
+  // Decompress the file either directly from memory or from a file input buffer.
+  tinfl_init(&inflator);
+
+  if (pZip->m_pState->m_pMem)
+  {
+    // Read directly from the archive in memory.
+    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
+    read_buf_size = read_buf_avail = file_stat.m_comp_size;
+    comp_remaining = 0;
+  }
+  else if (pUser_read_buf)
+  {
+    // Use a user provided read buffer.
+    if (!user_read_buf_size)
+      return MZ_FALSE;
+    pRead_buf = (mz_uint8 *)pUser_read_buf;
+    read_buf_size = user_read_buf_size;
+    read_buf_avail = 0;
+    comp_remaining = file_stat.m_comp_size;
+  }
+  else
+  {
+    // Temporarily allocate a read buffer.
+    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
+#ifdef _MSC_VER
+    if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
+#else
+    if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
+#endif
+      return MZ_FALSE;
+    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
+      return MZ_FALSE;
+    read_buf_avail = 0;
+    comp_remaining = file_stat.m_comp_size;
+  }
+
+  do
+  {
+    size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
+    if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
+    {
+      read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+      if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+      {
+        status = TINFL_STATUS_FAILED;
+        break;
+      }
+      cur_file_ofs += read_buf_avail;
+      comp_remaining -= read_buf_avail;
+      read_buf_ofs = 0;
+    }
+    in_buf_size = (size_t)read_buf_avail;
+    status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
+    read_buf_avail -= in_buf_size;
+    read_buf_ofs += in_buf_size;
+    out_buf_ofs += out_buf_size;
+  } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
+
+  if (status == TINFL_STATUS_DONE)
+  {
+    // Make sure the entire file was decompressed, and check its CRC.
+    if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32))
+      status = TINFL_STATUS_FAILED;
+  }
+
+  if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+
+  return status == TINFL_STATUS_DONE;
+}
+
+mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
+{
+  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
+  if (file_index < 0)
+    return MZ_FALSE;
+  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
+}
+
+mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
+{
+  return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
+}
+
+mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
+{
+  return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
+}
+
+void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
+{
+  mz_uint64 comp_size, uncomp_size, alloc_size;
+  const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index);
+  void *pBuf;
+
+  if (pSize)
+    *pSize = 0;
+  if (!p)
+    return NULL;
+
+  comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+  uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
+
+  alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
+#ifdef _MSC_VER
+  if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
+#else
+  if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
+#endif
+    return NULL;
+  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
+    return NULL;
+
+  if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
+  {
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+    return NULL;
+  }
+
+  if (pSize) *pSize = (size_t)alloc_size;
+  return pBuf;
+}
+
+void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
+{
+  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
+  if (file_index < 0)
+  {
+    if (pSize) *pSize = 0;
+    return MZ_FALSE;
+  }
+  return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);
+}
+
+mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
+{
+  int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT;
+  mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
+  mz_zip_archive_file_stat file_stat;
+  void *pRead_buf = NULL; void *pWrite_buf = NULL;
+  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+
+  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+    return MZ_FALSE;
+
+  // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes)
+  if (!file_stat.m_comp_size)
+    return MZ_TRUE;
+
+  // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers).
+  // I'm torn how to handle this case - should it fail instead?
+  if (mz_zip_reader_is_file_a_directory(pZip, file_index))
+    return MZ_TRUE;
+
+  // Encryption and patch files are not supported.
+  if (file_stat.m_bit_flag & (1 | 32))
+    return MZ_FALSE;
+
+  // This function only supports stored and deflate.
+  if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
+    return MZ_FALSE;
+
+  // Read and parse the local directory entry.
+  cur_file_ofs = file_stat.m_local_header_ofs;
+  if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+    return MZ_FALSE;
+
+  cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+  if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
+    return MZ_FALSE;
+
+  // Decompress the file either directly from memory or from a file input buffer.
+  if (pZip->m_pState->m_pMem)
+  {
+    pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
+    read_buf_size = read_buf_avail = file_stat.m_comp_size;
+    comp_remaining = 0;
+  }
+  else
+  {
+    read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE);
+    if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
+      return MZ_FALSE;
+    read_buf_avail = 0;
+    comp_remaining = file_stat.m_comp_size;
+  }
+
+  if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
+  {
+    // The file is stored or the caller has requested the compressed data.
+    if (pZip->m_pState->m_pMem)
+    {
+#ifdef _MSC_VER
+      if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
+#else
+      if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF))
+#endif
+        return MZ_FALSE;
+      if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
+        status = TINFL_STATUS_FAILED;
+      else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+        file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
+      cur_file_ofs += file_stat.m_comp_size;
+        #pragma unused(cur_file_ofs)
+      out_buf_ofs += file_stat.m_comp_size;
+      comp_remaining = 0;
+        #pragma unused(comp_remaining)
+    }
+    else
+    {
+      while (comp_remaining)
+      {
+        read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+        {
+          status = TINFL_STATUS_FAILED;
+          break;
+        }
+
+        if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+          file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
+
+        if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+        {
+          status = TINFL_STATUS_FAILED;
+          break;
+        }
+        cur_file_ofs += read_buf_avail;
+        out_buf_ofs += read_buf_avail;
+        comp_remaining -= read_buf_avail;
+      }
+    }
+  }
+  else
+  {
+    tinfl_decompressor inflator;
+    tinfl_init(&inflator);
+
+    if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
+      status = TINFL_STATUS_FAILED;
+    else
+    {
+      do
+      {
+        mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+        size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
+        if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
+        {
+          read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
+          if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
+          {
+            status = TINFL_STATUS_FAILED;
+            break;
+          }
+          cur_file_ofs += read_buf_avail;
+          comp_remaining -= read_buf_avail;
+          read_buf_ofs = 0;
+        }
+
+        in_buf_size = (size_t)read_buf_avail;
+        status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
+        read_buf_avail -= in_buf_size;
+        read_buf_ofs += in_buf_size;
+
+        if (out_buf_size)
+        {
+          if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
+          {
+            status = TINFL_STATUS_FAILED;
+            break;
+          }
+          file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
+          if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
+          {
+            status = TINFL_STATUS_FAILED;
+            break;
+          }
+        }
+      } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
+    }
+  }
+
+  if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
+  {
+    // Make sure the entire file was decompressed, and check its CRC.
+    if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32))
+      status = TINFL_STATUS_FAILED;
+  }
+
+  if (!pZip->m_pState->m_pMem)
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+  if (pWrite_buf)
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
+
+  return status == TINFL_STATUS_DONE;
+}
+
+mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
+{
+  int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags);
+  if (file_index < 0)
+    return MZ_FALSE;
+  return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
+{
+  (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque);
+}
+
+mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
+{
+  mz_bool status;
+  mz_zip_archive_file_stat file_stat;
+  MZ_FILE *pFile;
+  if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))
+    return MZ_FALSE;
+  pFile = MZ_FOPEN(pDst_filename, "wb");
+  if (!pFile)
+    return MZ_FALSE;
+  status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
+  if (MZ_FCLOSE(pFile) == EOF)
+    return MZ_FALSE;
+#ifndef MINIZ_NO_TIME
+  if (status)
+    mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
+#endif
+  return status;
+}
+#endif // #ifndef MINIZ_NO_STDIO
+
+mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
+{
+  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+    return MZ_FALSE;
+
+  if (pZip->m_pState)
+  {
+    mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL;
+    mz_zip_array_clear(pZip, &pState->m_central_dir);
+    mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
+    mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
+
+#ifndef MINIZ_NO_STDIO
+    if (pState->m_pFile)
+    {
+      MZ_FCLOSE(pState->m_pFile);
+      pState->m_pFile = NULL;
+    }
+#endif // #ifndef MINIZ_NO_STDIO
+
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+  }
+  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
+
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
+{
+  int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags);
+  if (file_index < 0)
+    return MZ_FALSE;
+  return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);
+}
+#endif
+
+// ------------------- .ZIP archive writing
+
+#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); }
+static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); }
+#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
+#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
+
+mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)
+{
+  if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
+    return MZ_FALSE;
+
+  if (pZip->m_file_offset_alignment)
+  {
+    // Ensure user specified file offset alignment is a power of 2.
+    if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
+      return MZ_FALSE;
+  }
+
+  if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func;
+  if (!pZip->m_pFree) pZip->m_pFree = def_free_func;
+  if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func;
+
+  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
+  pZip->m_archive_size = existing_size;
+  pZip->m_central_directory_file_ofs = 0;
+  pZip->m_total_files = 0;
+
+  if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
+    return MZ_FALSE;
+  memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
+  MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
+  return MZ_TRUE;
+}
+
+static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
+{
+  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+  mz_zip_internal_state *pState = pZip->m_pState;
+  mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
+#ifdef _MSC_VER
+  if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
+#else
+  if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)))
+#endif
+    return 0;
+  if (new_size > pState->m_mem_capacity)
+  {
+    void *pNew_block;
+    size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2;
+    if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
+      return 0;
+    pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity;
+  }
+  memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
+  pState->m_mem_size = (size_t)new_size;
+  return n;
+}
+
+mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)
+{
+  pZip->m_pWrite = mz_zip_heap_write_func;
+  pZip->m_pIO_opaque = pZip;
+  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
+    return MZ_FALSE;
+  if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
+  {
+    if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
+    {
+      mz_zip_writer_end(pZip);
+      return MZ_FALSE;
+    }
+    pZip->m_pState->m_mem_capacity = initial_allocation_size;
+  }
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
+{
+  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
+  mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
+  if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
+    return 0;
+  return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
+}
+
+mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)
+{
+  MZ_FILE *pFile;
+  pZip->m_pWrite = mz_zip_file_write_func;
+  pZip->m_pIO_opaque = pZip;
+  if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning))
+    return MZ_FALSE;
+  if (NULL == (pFile = MZ_FOPEN(pFilename, "wb")))
+  {
+    mz_zip_writer_end(pZip);
+    return MZ_FALSE;
+  }
+  pZip->m_pState->m_pFile = pFile;
+  if (size_to_reserve_at_beginning)
+  {
+    mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf);
+    do
+    {
+      size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
+      if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
+      {
+        mz_zip_writer_end(pZip);
+        return MZ_FALSE;
+      }
+      cur_ofs += n; size_to_reserve_at_beginning -= n;
+    } while (size_to_reserve_at_beginning);
+  }
+  return MZ_TRUE;
+}
+#endif // #ifndef MINIZ_NO_STDIO
+
+mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)
+{
+  mz_zip_internal_state *pState;
+  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
+    return MZ_FALSE;
+  // No sense in trying to write to an archive that's already at the support max size
+  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  pState = pZip->m_pState;
+
+  if (pState->m_pFile)
+  {
+#ifdef MINIZ_NO_STDIO
+    pFilename; return MZ_FALSE;
+#else
+    // Archive is being read from stdio - try to reopen as writable.
+    if (pZip->m_pIO_opaque != pZip)
+      return MZ_FALSE;
+    if (!pFilename)
+      return MZ_FALSE;
+    pZip->m_pWrite = mz_zip_file_write_func;
+    if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
+    {
+      // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
+      mz_zip_reader_end(pZip);
+      return MZ_FALSE;
+    }
+#endif // #ifdef MINIZ_NO_STDIO
+  }
+  else if (pState->m_pMem)
+  {
+    // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
+    if (pZip->m_pIO_opaque != pZip)
+      return MZ_FALSE;
+    pState->m_mem_capacity = pState->m_mem_size;
+    pZip->m_pWrite = mz_zip_heap_write_func;
+  }
+  // Archive is being read via a user provided read function - make sure the user has specified a write function too.
+  else if (!pZip->m_pWrite)
+    return MZ_FALSE;
+
+  // Start writing new files at the archive's current central directory location.
+  pZip->m_archive_size = pZip->m_central_directory_file_ofs;
+  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
+  pZip->m_central_directory_file_ofs = 0;
+
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
+{
+  return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
+}
+
+typedef struct
+{
+  mz_zip_archive *m_pZip;
+  mz_uint64 m_cur_archive_file_ofs;
+  mz_uint64 m_comp_size;
+} mz_zip_writer_add_state;
+
+static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser)
+{
+  mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
+  if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
+    return MZ_FALSE;
+  pState->m_cur_archive_file_ofs += len;
+  pState->m_comp_size += len;
+  return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
+{
+  (void)pZip;
+  memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
+  return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
+{
+  (void)pZip;
+  memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
+  MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
+  MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs);
+  return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
+{
+  mz_zip_internal_state *pState = pZip->m_pState;
+  mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
+  size_t orig_central_dir_size = pState->m_central_dir.m_size;
+  mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
+
+  // No zip64 support yet
+  if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
+    return MZ_FALSE;
+
+  if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
+      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
+      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
+      (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
+      (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
+  {
+    // Try to push the central directory array back into its original state.
+    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+    return MZ_FALSE;
+  }
+
+  return MZ_TRUE;
+}
+
+static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
+{
+  // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
+  if (*pArchive_name == '/')
+    return MZ_FALSE;
+  while (*pArchive_name)
+  {
+    if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
+      return MZ_FALSE;
+    pArchive_name++;
+  }
+  return MZ_TRUE;
+}
+
+static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
+{
+  mz_uint32 n;
+  if (!pZip->m_file_offset_alignment)
+    return 0;
+  n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
+  return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1);
+}
+
+static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
+{
+  char buf[4096];
+  memset(buf, 0, MZ_MIN(sizeof(buf), n));
+  while (n)
+  {
+    mz_uint32 s = MZ_MIN(sizeof(buf), n);
+    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
+      return MZ_FALSE;
+    cur_file_ofs += s; n -= s;
+  }
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
+{
+  mz_uint16 method = 0, dos_time = 0, dos_date = 0;
+  mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
+  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;
+  size_t archive_name_size;
+  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
+  tdefl_compressor *pComp = NULL;
+  mz_bool store_data_uncompressed;
+  mz_zip_internal_state *pState;
+
+  if ((int)level_and_flags < 0)
+    level_and_flags = MZ_DEFAULT_LEVEL;
+  level = level_and_flags & 0xF;
+  store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
+
+  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION))
+    return MZ_FALSE;
+
+  pState = pZip->m_pState;
+
+  if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
+    return MZ_FALSE;
+  // No zip64 support yet
+  if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
+    return MZ_FALSE;
+  if (!mz_zip_writer_validate_archive_name(pArchive_name))
+    return MZ_FALSE;
+
+#ifndef MINIZ_NO_TIME
+  {
+    time_t cur_time; time(&cur_time);
+    mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date);
+  }
+#endif // #ifndef MINIZ_NO_TIME
+
+  archive_name_size = strlen(pArchive_name);
+  if (archive_name_size > 0xFFFF)
+    return MZ_FALSE;
+
+  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+  // no zip64 support yet
+  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
+  {
+    // Set DOS Subdirectory attribute bit.
+    ext_attributes |= 0x10;
+    // Subdirectories cannot contain data.
+    if ((buf_size) || (uncomp_size))
+      return MZ_FALSE;
+  }
+
+  // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
+  if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
+    return MZ_FALSE;
+
+  if ((!store_data_uncompressed) && (buf_size))
+  {
+    if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
+      return MZ_FALSE;
+  }
+
+  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
+  {
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+    return MZ_FALSE;
+  }
+  local_dir_header_ofs += num_alignment_padding_bytes;
+  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
+  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
+
+  MZ_CLEAR_OBJ(local_dir_header);
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+  {
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+    return MZ_FALSE;
+  }
+  cur_archive_file_ofs += archive_name_size;
+
+  if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
+  {
+    uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size);
+    uncomp_size = buf_size;
+    if (uncomp_size <= 3)
+    {
+      level = 0;
+      store_data_uncompressed = MZ_TRUE;
+    }
+  }
+
+  if (store_data_uncompressed)
+  {
+    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+      return MZ_FALSE;
+    }
+
+    cur_archive_file_ofs += buf_size;
+    comp_size = buf_size;
+
+    if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
+      method = MZ_DEFLATED;
+  }
+  else if (buf_size)
+  {
+    mz_zip_writer_add_state state;
+
+    state.m_pZip = pZip;
+    state.m_cur_archive_file_ofs = cur_archive_file_ofs;
+    state.m_comp_size = 0;
+
+    if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
+        (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+      return MZ_FALSE;
+    }
+
+    comp_size = state.m_comp_size;
+    cur_archive_file_ofs = state.m_cur_archive_file_ofs;
+
+    method = MZ_DEFLATED;
+  }
+
+  pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+  pComp = NULL;
+
+  // no zip64 support yet
+  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
+    return MZ_FALSE;
+
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+    return MZ_FALSE;
+
+  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
+    return MZ_FALSE;
+
+  pZip->m_total_files++;
+  pZip->m_archive_size = cur_archive_file_ofs;
+
+  return MZ_TRUE;
+}
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
+{
+  mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
+  mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
+  mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
+  size_t archive_name_size;
+  mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
+  MZ_FILE *pSrc_file = NULL;
+
+  if ((int)level_and_flags < 0)
+    level_and_flags = MZ_DEFAULT_LEVEL;
+  level = level_and_flags & 0xF;
+
+  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
+    return MZ_FALSE;
+  if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
+    return MZ_FALSE;
+  if (!mz_zip_writer_validate_archive_name(pArchive_name))
+    return MZ_FALSE;
+
+  archive_name_size = strlen(pArchive_name);
+  if (archive_name_size > 0xFFFF)
+    return MZ_FALSE;
+
+  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+  // no zip64 support yet
+  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
+    return MZ_FALSE;
+    
+  pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
+  if (!pSrc_file)
+    return MZ_FALSE;
+  MZ_FSEEK64(pSrc_file, 0, SEEK_END);
+  uncomp_size = MZ_FTELL64(pSrc_file);
+  MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
+
+  if (uncomp_size > 0xFFFFFFFF)
+  {
+    // No zip64 support yet
+    MZ_FCLOSE(pSrc_file);
+    return MZ_FALSE;
+  }
+  if (uncomp_size <= 3)
+    level = 0;
+
+  if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
+  {
+    MZ_FCLOSE(pSrc_file);
+    return MZ_FALSE;
+  }
+  local_dir_header_ofs += num_alignment_padding_bytes;
+  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
+  cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
+
+  MZ_CLEAR_OBJ(local_dir_header);
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
+  {
+    MZ_FCLOSE(pSrc_file);
+    return MZ_FALSE;
+  }
+  cur_archive_file_ofs += archive_name_size;
+
+  if (uncomp_size)
+  {
+    mz_uint64 uncomp_remaining = uncomp_size;
+    void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
+    if (!pRead_buf)
+    {
+      MZ_FCLOSE(pSrc_file);
+      return MZ_FALSE;
+    }
+
+    if (!level)
+    {
+      while (uncomp_remaining)
+      {
+        mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
+        if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
+        {
+          pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+          MZ_FCLOSE(pSrc_file);
+          return MZ_FALSE;
+        }
+        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
+        uncomp_remaining -= n;
+        cur_archive_file_ofs += n;
+      }
+      comp_size = uncomp_size;
+    }
+    else
+    {
+      mz_bool result = MZ_FALSE;
+      mz_zip_writer_add_state state;
+      tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
+      if (!pComp)
+      {
+        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+        MZ_FCLOSE(pSrc_file);
+        return MZ_FALSE;
+      }
+
+      state.m_pZip = pZip;
+      state.m_cur_archive_file_ofs = cur_archive_file_ofs;
+      state.m_comp_size = 0;
+
+      if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
+      {
+        pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+        MZ_FCLOSE(pSrc_file);
+        return MZ_FALSE;
+      }
+
+      for ( ; ; )
+      {
+        size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
+        tdefl_status status;
+
+        if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
+          break;
+
+        uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
+        uncomp_remaining -= in_buf_size;
+
+        status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
+        if (status == TDEFL_STATUS_DONE)
+        {
+          result = MZ_TRUE;
+          break;
+        }
+        else if (status != TDEFL_STATUS_OKAY)
+          break;
+      }
+
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
+
+      if (!result)
+      {
+        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+        MZ_FCLOSE(pSrc_file);
+        return MZ_FALSE;
+      }
+
+      comp_size = state.m_comp_size;
+      cur_archive_file_ofs = state.m_cur_archive_file_ofs;
+
+      method = MZ_DEFLATED;
+    }
+
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
+  }
+
+  MZ_FCLOSE(pSrc_file); pSrc_file = NULL;
+
+  // no zip64 support yet
+  if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
+    return MZ_FALSE;
+
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
+    return MZ_FALSE;
+
+  if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
+    return MZ_FALSE;
+
+  pZip->m_total_files++;
+  pZip->m_archive_size = cur_archive_file_ofs;
+
+  return MZ_TRUE;
+}
+#endif // #ifndef MINIZ_NO_STDIO
+
+mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index)
+{
+  mz_uint n, bit_flags, num_alignment_padding_bytes;
+  mz_uint64 comp_bytes_remaining, local_dir_header_ofs;
+  mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
+  mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
+  mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
+  size_t orig_central_dir_size;
+  mz_zip_internal_state *pState;
+  void *pBuf; const mz_uint8 *pSrc_central_header;
+
+  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
+    return MZ_FALSE;
+  if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index)))
+    return MZ_FALSE;
+  pState = pZip->m_pState;
+
+  num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
+
+  // no zip64 support yet
+  if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
+  cur_dst_file_ofs = pZip->m_archive_size;
+
+  if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
+    return MZ_FALSE;
+  cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
+
+  if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
+    return MZ_FALSE;
+  cur_dst_file_ofs += num_alignment_padding_bytes;
+  local_dir_header_ofs = cur_dst_file_ofs;
+  if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
+
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
+    return MZ_FALSE;
+  cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
+
+  n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
+  comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
+
+  if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining)))))
+    return MZ_FALSE;
+
+  while (comp_bytes_remaining)
+  {
+    n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining);
+    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+      return MZ_FALSE;
+    }
+    cur_src_file_ofs += n;
+
+    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+      return MZ_FALSE;
+    }
+    cur_dst_file_ofs += n;
+
+    comp_bytes_remaining -= n;
+  }
+
+  bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
+  if (bit_flags & 8)
+  {
+    // Copy data descriptor
+    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+      return MZ_FALSE;
+    }
+
+    n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3);
+    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
+    {
+      pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+      return MZ_FALSE;
+    }
+
+    cur_src_file_ofs += n;
+      #pragma unused(cur_src_file_ofs)
+    cur_dst_file_ofs += n;
+  }
+  pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
+
+  // no zip64 support yet
+  if (cur_dst_file_ofs > 0xFFFFFFFF)
+    return MZ_FALSE;
+
+  orig_central_dir_size = pState->m_central_dir.m_size;
+
+  memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
+  MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
+  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
+    return MZ_FALSE;
+
+  n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
+  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n))
+  {
+    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+    return MZ_FALSE;
+  }
+
+  if (pState->m_central_dir.m_size > 0xFFFFFFFF)
+    return MZ_FALSE;
+  n = (mz_uint32)orig_central_dir_size;
+  if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
+  {
+    mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
+    return MZ_FALSE;
+  }
+
+  pZip->m_total_files++;
+  pZip->m_archive_size = cur_dst_file_ofs;
+
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
+{
+  mz_zip_internal_state *pState;
+  mz_uint64 central_dir_ofs, central_dir_size;
+  mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE];
+
+  if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
+    return MZ_FALSE;
+
+  pState = pZip->m_pState;
+
+  // no zip64 support yet
+  if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF))
+    return MZ_FALSE;
+
+  central_dir_ofs = 0;
+  central_dir_size = 0;
+  if (pZip->m_total_files)
+  {
+    // Write central directory
+    central_dir_ofs = pZip->m_archive_size;
+    central_dir_size = pState->m_central_dir.m_size;
+    pZip->m_central_directory_file_ofs = central_dir_ofs;
+    if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
+      return MZ_FALSE;
+    pZip->m_archive_size += central_dir_size;
+  }
+
+  // Write end of central directory record
+  MZ_CLEAR_OBJ(hdr);
+  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
+  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
+  MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
+  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size);
+  MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs);
+
+  if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr))
+    return MZ_FALSE;
+#ifndef MINIZ_NO_STDIO
+  if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
+    return MZ_FALSE;
+#endif // #ifndef MINIZ_NO_STDIO
+
+  pZip->m_archive_size += sizeof(hdr);
+
+  pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize)
+{
+  if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize))
+    return MZ_FALSE;
+  if (pZip->m_pWrite != mz_zip_heap_write_func)
+    return MZ_FALSE;
+  if (!mz_zip_writer_finalize_archive(pZip))
+    return MZ_FALSE;
+
+  *pBuf = pZip->m_pState->m_pMem;
+  *pSize = pZip->m_pState->m_mem_size;
+  pZip->m_pState->m_pMem = NULL;
+  pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
+  return MZ_TRUE;
+}
+
+mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
+{
+  mz_zip_internal_state *pState;
+  mz_bool status = MZ_TRUE;
+  if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
+    return MZ_FALSE;
+
+  pState = pZip->m_pState;
+  pZip->m_pState = NULL;
+  mz_zip_array_clear(pZip, &pState->m_central_dir);
+  mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
+  mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
+
+#ifndef MINIZ_NO_STDIO
+  if (pState->m_pFile)
+  {
+    MZ_FCLOSE(pState->m_pFile);
+    pState->m_pFile = NULL;
+  }
+#endif // #ifndef MINIZ_NO_STDIO
+
+  if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
+  {
+    pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
+    pState->m_pMem = NULL;
+  }
+
+  pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
+  pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
+  return status;
+}
+
+#ifndef MINIZ_NO_STDIO
+mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
+{
+  mz_bool status, created_new_archive = MZ_FALSE;
+  mz_zip_archive zip_archive;
+  struct MZ_FILE_STAT_STRUCT file_stat;
+  MZ_CLEAR_OBJ(zip_archive);
+  if ((int)level_and_flags < 0)
+     level_and_flags = MZ_DEFAULT_LEVEL;
+  if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
+    return MZ_FALSE;
+  if (!mz_zip_writer_validate_archive_name(pArchive_name))
+    return MZ_FALSE;
+  if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
+  {
+    // Create a new archive.
+    if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0))
+      return MZ_FALSE;
+    created_new_archive = MZ_TRUE;
+  }
+  else
+  {
+    // Append to an existing archive.
+    if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
+      return MZ_FALSE;
+    if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename))
+    {
+      mz_zip_reader_end(&zip_archive);
+      return MZ_FALSE;
+    }
+  }
+  status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
+  // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
+  if (!mz_zip_writer_finalize_archive(&zip_archive))
+    status = MZ_FALSE;
+  if (!mz_zip_writer_end(&zip_archive))
+    status = MZ_FALSE;
+  if ((!status) && (created_new_archive))
+  {
+    // It's a new archive and something went wrong, so just delete it.
+    int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
+    (void)ignoredStatus;
+  }
+  return status;
+}
+
+void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)
+{
+  int file_index;
+  mz_zip_archive zip_archive;
+  void *p = NULL;
+
+  if (pSize)
+    *pSize = 0;
+
+  if ((!pZip_filename) || (!pArchive_name))
+    return NULL;
+
+  MZ_CLEAR_OBJ(zip_archive);
+  if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY))
+    return NULL;
+
+  if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0)
+    p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);
+
+  mz_zip_reader_end(&zip_archive);
+  return p;
+}
+
+#endif // #ifndef MINIZ_NO_STDIO
+
+#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
+
+#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // MINIZ_HEADER_FILE_ONLY
+
+/*
+  This is free and unencumbered software released into the public domain.
+
+  Anyone is free to copy, modify, publish, use, compile, sell, or
+  distribute this software, either in source code form or as a compiled
+  binary, for any purpose, commercial or non-commercial, and by any
+  means.
+
+  In jurisdictions that recognize copyright laws, the author or authors
+  of this software dedicate any and all copyright interest in the
+  software to the public domain. We make this dedication for the benefit
+  of the public at large and to the detriment of our heirs and
+  successors. We intend this dedication to be an overt act of
+  relinquishment in perpetuity of all present and future rights to this
+  software under copyright law.
+
+  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 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.
+
+  For more information, please refer to <http://unlicense.org/>
+*/
diff --git a/console/nifti1.h b/console/nifti1.h
new file mode 100644
index 0000000..d2c6539
--- /dev/null
+++ b/console/nifti1.h
@@ -0,0 +1,1491 @@
+/** \file nifti1.h
+    \brief Official definition of the nifti1 header.  Written by Bob Cox, SSCC, NIMH.
+
+    HISTORY:
+
+        29 Nov 2007 [rickr]
+           - added DT_RGBA32 and NIFTI_TYPE_RGBA32
+           - added NIFTI_INTENT codes:
+                TIME_SERIES, NODE_INDEX, RGB_VECTOR, RGBA_VECTOR, SHAPE
+ */
+
+#ifndef _NIFTI_HEADER_
+#define _NIFTI_HEADER_
+
+/*****************************************************************************
+      ** This file defines the "NIFTI-1" header format.               **
+      ** It is derived from 2 meetings at the NIH (31 Mar 2003 and    **
+      ** 02 Sep 2003) of the Data Format Working Group (DFWG),        **
+      ** chartered by the NIfTI (Neuroimaging Informatics Technology  **
+      ** Initiative) at the National Institutes of Health (NIH).      **
+      **--------------------------------------------------------------**
+      ** Neither the National Institutes of Health (NIH), the DFWG,   **
+      ** nor any of the members or employees of these institutions    **
+      ** imply any warranty of usefulness of this material for any    **
+      ** purpose, and do not assume any liability for damages,        **
+      ** incidental or otherwise, caused by any use of this document. **
+      ** If these conditions are not acceptable, do not use this!     **
+      **--------------------------------------------------------------**
+      ** Author:   Robert W Cox (NIMH, Bethesda)                      **
+      ** Advisors: John Ashburner (FIL, London),                      **
+      **           Stephen Smith (FMRIB, Oxford),                     **
+      **           Mark Jenkinson (FMRIB, Oxford)                     **
+******************************************************************************/
+
+/*---------------------------------------------------------------------------*/
+/* Note that the ANALYZE 7.5 file header (dbh.h) is
+         (c) Copyright 1986-1995
+         Biomedical Imaging Resource
+         Mayo Foundation
+   Incorporation of components of dbh.h are by permission of the
+   Mayo Foundation.
+
+   Changes from the ANALYZE 7.5 file header in this file are released to the
+   public domain, including the functional comments and any amusing asides.
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/*! INTRODUCTION TO NIFTI-1:
+   ------------------------
+   The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5
+   format are:
+    (a) To add information to the header that will be useful for functional
+        neuroimaging data analysis and display.  These additions include:
+        - More basic data types.
+        - Two affine transformations to specify voxel coordinates.
+        - "Intent" codes and parameters to describe the meaning of the data.
+        - Affine scaling of the stored data values to their "true" values.
+        - Optional storage of the header and image data in one file (.nii).
+    (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible
+        software (i.e., such a program should be able to do something useful
+        with a NIFTI-1 dataset -- at least, with one stored in a traditional
+        .img/.hdr file pair).
+
+   Most of the unused fields in the ANALYZE 7.5 header have been taken,
+   and some of the lesser-used fields have been co-opted for other purposes.
+   Notably, most of the data_history substructure has been co-opted for
+   other purposes, since the ANALYZE 7.5 format describes this substructure
+   as "not required".
+
+   NIFTI-1 FLAG (MAGIC STRINGS):
+   ----------------------------
+   To flag such a struct as being conformant to the NIFTI-1 spec, the last 4
+   bytes of the header must be either the C String "ni1" or "n+1";
+   in hexadecimal, the 4 bytes
+     6E 69 31 00   or   6E 2B 31 00
+   (in any future version of this format, the '1' will be upgraded to '2',
+   etc.).  Normally, such a "magic number" or flag goes at the start of the
+   file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to
+   putting this marker last.  However, recall that "the last shall be first"
+   (Matthew 20:16).
+
+   If a NIFTI-aware program reads a header file that is NOT marked with a
+   NIFTI magic string, then it should treat the header as an ANALYZE 7.5
+   structure.
+
+   NIFTI-1 FILE STORAGE:
+   --------------------
+   "ni1" means that the image data is stored in the ".img" file corresponding
+   to the header file (starting at file offset 0).
+
+   "n+1" means that the image data is stored in the same file as the header
+   information.  We recommend that the combined header+data filename suffix
+   be ".nii".  When the dataset is stored in one file, the first byte of image
+   data is stored at byte location (int)vox_offset in this combined file.
+   The minimum allowed value of vox_offset is 352; for compatibility with
+   some software, vox_offset should be an integral multiple of 16.
+
+   GRACE UNDER FIRE:
+   ----------------
+   Most NIFTI-aware programs will only be able to handle a subset of the full
+   range of datasets possible with this format.  All NIFTI-aware programs
+   should take care to check if an input dataset conforms to the program's
+   needs and expectations (e.g., check datatype, intent_code, etc.).  If the
+   input dataset can't be handled by the program, the program should fail
+   gracefully (e.g., print a useful warning; not crash).
+
+   SAMPLE CODES:
+   ------------
+   The associated files nifti1_io.h and nifti1_io.c provide a sample
+   implementation in C of a set of functions to read, write, and manipulate
+   NIFTI-1 files.  The file nifti1_test.c is a sample program that uses
+   the nifti1_io.c functions.
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* HEADER STRUCT DECLARATION:
+   -------------------------
+   In the comments below for each field, only NIFTI-1 specific requirements
+   or changes from the ANALYZE 7.5 format are described.  For convenience,
+   the 348 byte header is described as a single struct, rather than as the
+   ANALYZE 7.5 group of 3 substructs.
+
+   Further comments about the interpretation of various elements of this
+   header are after the data type definition itself.  Fields that are
+   marked as ++UNUSED++ have no particular interpretation in this standard.
+   (Also see the UNUSED FIELDS comment section, far below.)
+
+   The presumption below is that the various C types have particular sizes:
+     sizeof(int) = sizeof(float) = 4 ;  sizeof(short) = 2
+-----------------------------------------------------------------------------*/
+
+/*=================*/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+/*=================*/
+
+/*! \struct nifti_1_header
+    \brief Data structure defining the fields in the nifti1 header.
+           This binary header should be found at the beginning of a valid
+           NIFTI-1 header file.
+ */
+                        /*************************/  /************************/
+struct nifti_1_header { /* NIFTI-1 usage         */  /* ANALYZE 7.5 field(s) */
+//typedef struct __attribute__((packed))
+                        /*************************/  /************************/
+
+                                           /*--- was header_key substruct ---*/
+ int   sizeof_hdr;    /*!< MUST be 348           */  /* int sizeof_hdr;      */
+ char  data_type[10]; /*!< ++UNUSED++            */  /* char data_type[10];  */
+ char  db_name[18];   /*!< ++UNUSED++            */  /* char db_name[18];    */
+ int   extents;       /*!< ++UNUSED++            */  /* int extents;         */
+ short session_error; /*!< ++UNUSED++            */  /* short session_error; */
+ char  regular;       /*!< ++UNUSED++            */  /* char regular;        */
+ char  dim_info;      /*!< MRI slice ordering.   */  /* char hkey_un0;       */
+
+                                      /*--- was image_dimension substruct ---*/
+ short dim[8];        /*!< Data array dimensions.*/  /* short dim[8];        */
+ float intent_p1 ;    /*!< 1st intent parameter. */  /* short unused8;       */
+                                                     /* short unused9;       */
+ float intent_p2 ;    /*!< 2nd intent parameter. */  /* short unused10;      */
+                                                     /* short unused11;      */
+ float intent_p3 ;    /*!< 3rd intent parameter. */  /* short unused12;      */
+                                                     /* short unused13;      */
+ short intent_code ;  /*!< NIFTI_INTENT_* code.  */  /* short unused14;      */
+ short datatype;      /*!< Defines data type!    */  /* short datatype;      */
+ short bitpix;        /*!< Number bits/voxel.    */  /* short bitpix;        */
+ short slice_start;   /*!< First slice index.    */  /* short dim_un0;       */
+ float pixdim[8];     /*!< Grid spacings.        */  /* float pixdim[8];     */
+ float vox_offset;    /*!< Offset into .nii file */  /* float vox_offset;    */
+ float scl_slope ;    /*!< Data scaling: slope.  */  /* float funused1;      */
+ float scl_inter ;    /*!< Data scaling: offset. */  /* float funused2;      */
+ short slice_end;     /*!< Last slice index.     */  /* float funused3;      */
+ char  slice_code ;   /*!< Slice timing order.   */
+ char  xyzt_units ;   /*!< Units of pixdim[1..4] */
+ float cal_max;       /*!< Max display intensity */  /* float cal_max;       */
+ float cal_min;       /*!< Min display intensity */  /* float cal_min;       */
+ float slice_duration;/*!< Time for 1 slice.     */  /* float compressed;    */
+ float toffset;       /*!< Time axis shift.      */  /* float verified;      */
+ int   glmax;         /*!< ++UNUSED++            */  /* int glmax;           */
+ int   glmin;         /*!< ++UNUSED++            */  /* int glmin;           */
+
+                                         /*--- was data_history substruct ---*/
+ char  descrip[80];   /*!< any text you like.    */  /* char descrip[80];    */
+ char  aux_file[24];  /*!< auxiliary filename.   */  /* char aux_file[24];   */
+
+ short qform_code ;   /*!< NIFTI_XFORM_* code.   */  /*-- all ANALYZE 7.5 ---*/
+ short sform_code ;   /*!< NIFTI_XFORM_* code.   */  /*   fields below here  */
+                                                     /*   are replaced       */
+ float quatern_b ;    /*!< Quaternion b param.   */
+ float quatern_c ;    /*!< Quaternion c param.   */
+ float quatern_d ;    /*!< Quaternion d param.   */
+ float qoffset_x ;    /*!< Quaternion x shift.   */
+ float qoffset_y ;    /*!< Quaternion y shift.   */
+ float qoffset_z ;    /*!< Quaternion z shift.   */
+
+ float srow_x[4] ;    /*!< 1st row affine transform.   */
+ float srow_y[4] ;    /*!< 2nd row affine transform.   */
+ float srow_z[4] ;    /*!< 3rd row affine transform.   */
+
+ char intent_name[16];/*!< 'name' or meaning of data.  */
+
+ char magic[4] ;      /*!< MUST be "ni1\0" or "n+1\0". */
+
+} ;                   /**** 348 bytes total ****/
+
+typedef struct nifti_1_header nifti_1_header ;
+
+/*---------------------------------------------------------------------------*/
+/* HEADER EXTENSIONS:
+   -----------------
+   After the end of the 348 byte header (e.g., after the magic field),
+   the next 4 bytes are a char array field named "extension". By default,
+   all 4 bytes of this array should be set to zero. In a .nii file, these
+   4 bytes will always be present, since the earliest start point for
+   the image data is byte #352. In a separate .hdr file, these bytes may
+   or may not be present. If not present (i.e., if the length of the .hdr
+   file is 348 bytes), then a NIfTI-1 compliant program should use the
+   default value of extension={0,0,0,0}. The first byte (extension[0])
+   is the only value of this array that is specified at present. The other
+   3 bytes are reserved for future use.
+
+   If extension[0] is nonzero, it indicates that extended header information
+   is present in the bytes following the extension array. In a .nii file,
+   this extended header data is before the image data (and vox_offset
+   must be set correctly to allow for this). In a .hdr file, this extended
+   data follows extension and proceeds (potentially) to the end of the file.
+
+   The format of extended header data is weakly specified. Each extension
+   must be an integer multiple of 16 bytes long. The first 8 bytes of each
+   extension comprise 2 integers:
+      int esize , ecode ;
+   These values may need to be byte-swapped, as indicated by dim[0] for
+   the rest of the header.
+     * esize is the number of bytes that form the extended header data
+       + esize must be a positive integral multiple of 16
+       + this length includes the 8 bytes of esize and ecode themselves
+     * ecode is a non-negative integer that indicates the format of the
+       extended header data that follows
+       + different ecode values are assigned to different developer groups
+       + at present, the "registered" values for code are
+         = 0 = unknown private format (not recommended!)
+         = 2 = DICOM format (i.e., attribute tags and values)
+         = 4 = AFNI group (i.e., ASCII XML-ish elements)
+   In the interests of interoperability (a primary rationale for NIfTI),
+   groups developing software that uses this extension mechanism are
+   encouraged to document and publicize the format of their extensions.
+   To this end, the NIfTI DFWG will assign even numbered codes upon request
+   to groups submitting at least rudimentary documentation for the format
+   of their extension; at present, the contact is mailto:rwcox at nih.gov.
+   The assigned codes and documentation will be posted on the NIfTI
+   website. All odd values of ecode (and 0) will remain unassigned;
+   at least, until the even ones are used up, when we get to 2,147,483,646.
+
+   Note that the other contents of the extended header data section are
+   totally unspecified by the NIfTI-1 standard. In particular, if binary
+   data is stored in such a section, its byte order is not necessarily
+   the same as that given by examining dim[0]; it is incumbent on the
+   programs dealing with such data to determine the byte order of binary
+   extended header data.
+
+   Multiple extended header sections are allowed, each starting with an
+   esize,ecode value pair. The first esize value, as described above,
+   is at bytes #352-355 in the .hdr or .nii file (files start at byte #0).
+   If this value is positive, then the second (esize2) will be found
+   starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2,
+   et cetera.  Of course, in a .nii file, the value of vox_offset must
+   be compatible with these extensions. If a malformed file indicates
+   that an extended header data section would run past vox_offset, then
+   the entire extended header section should be ignored. In a .hdr file,
+   if an extended header data section would run past the end-of-file,
+   that extended header data should also be ignored.
+
+   With the above scheme, a program can successively examine the esize
+   and ecode values, and skip over each extended header section if the
+   program doesn't know how to interpret the data within. Of course, any
+   program can simply ignore all extended header sections simply by jumping
+   straight to the image data using vox_offset.
+-----------------------------------------------------------------------------*/
+   
+/*! \struct nifti1_extender
+    \brief This structure represents a 4-byte string that should follow the
+           binary nifti_1_header data in a NIFTI-1 header file.  If the char
+           values are {1,0,0,0}, the file is expected to contain extensions,
+           values of {0,0,0,0} imply the file does not contain extensions.
+           Other sequences of values are not currently defined.
+ */
+struct nifti1_extender { char extension[4] ; } ;
+typedef struct nifti1_extender nifti1_extender ;
+
+/*! \struct nifti1_extension
+    \brief Data structure defining the fields of a header extension.
+ */
+struct nifti1_extension {
+   int    esize ; /*!< size of extension, in bytes (must be multiple of 16) */
+   int    ecode ; /*!< extension code, one of the NIFTI_ECODE_ values       */
+   char * edata ; /*!< raw data, with no byte swapping (length is esize-8)  */
+} ;
+typedef struct nifti1_extension nifti1_extension ;
+
+/*---------------------------------------------------------------------------*/
+/* DATA DIMENSIONALITY (as in ANALYZE 7.5):
+   ---------------------------------------
+     dim[0] = number of dimensions;
+              - if dim[0] is outside range 1..7, then the header information
+                needs to be byte swapped appropriately
+              - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves
+                dimensions 1,2,3 for space (x,y,z), 4 for time (t), and
+                5,6,7 for anything else needed.
+
+     dim[i] = length of dimension #i, for i=1..dim[0]  (must be positive)
+              - also see the discussion of intent_code, far below
+
+     pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive)
+                 - cf. ORIENTATION section below for use of pixdim[0]
+                 - the units of pixdim can be specified with the xyzt_units
+                   field (also described far below).
+
+   Number of bits per voxel value is in bitpix, which MUST correspond with
+   the datatype field.  The total number of bytes in the image data is
+     dim[1] * ... * dim[dim[0]] * bitpix / 8
+
+   In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time,
+   and dimension 5 is for storing multiple values at each spatiotemporal
+   voxel.  Some examples:
+     - A typical whole-brain FMRI experiment's time series:
+        - dim[0] = 4
+        - dim[1] = 64   pixdim[1] = 3.75 xyzt_units =  NIFTI_UNITS_MM
+        - dim[2] = 64   pixdim[2] = 3.75             | NIFTI_UNITS_SEC
+        - dim[3] = 20   pixdim[3] = 5.0
+        - dim[4] = 120  pixdim[4] = 2.0
+     - A typical T1-weighted anatomical volume:
+        - dim[0] = 3
+        - dim[1] = 256  pixdim[1] = 1.0  xyzt_units = NIFTI_UNITS_MM
+        - dim[2] = 256  pixdim[2] = 1.0
+        - dim[3] = 128  pixdim[3] = 1.1
+     - A single slice EPI time series:
+        - dim[0] = 4
+        - dim[1] = 64   pixdim[1] = 3.75 xyzt_units =  NIFTI_UNITS_MM
+        - dim[2] = 64   pixdim[2] = 3.75             | NIFTI_UNITS_SEC
+        - dim[3] = 1    pixdim[3] = 5.0
+        - dim[4] = 1200 pixdim[4] = 0.2
+     - A 3-vector stored at each point in a 3D volume:
+        - dim[0] = 5
+        - dim[1] = 256  pixdim[1] = 1.0  xyzt_units = NIFTI_UNITS_MM
+        - dim[2] = 256  pixdim[2] = 1.0
+        - dim[3] = 128  pixdim[3] = 1.1
+        - dim[4] = 1    pixdim[4] = 0.0
+        - dim[5] = 3                     intent_code = NIFTI_INTENT_VECTOR
+     - A single time series with a 3x3 matrix at each point:
+        - dim[0] = 5
+        - dim[1] = 1                     xyzt_units = NIFTI_UNITS_SEC
+        - dim[2] = 1
+        - dim[3] = 1
+        - dim[4] = 1200 pixdim[4] = 0.2
+        - dim[5] = 9                     intent_code = NIFTI_INTENT_GENMATRIX
+        - intent_p1 = intent_p2 = 3.0    (indicates matrix dimensions)
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* DATA STORAGE:
+   ------------
+   If the magic field is "n+1", then the voxel data is stored in the
+   same file as the header.  In this case, the voxel data starts at offset
+   (int)vox_offset into the header file.  Thus, vox_offset=352.0 means that
+   the data starts immediately after the NIFTI-1 header.  If vox_offset is
+   greater than 352, the NIFTI-1 format does not say much about the
+   contents of the dataset file between the end of the header and the
+   start of the data.
+
+   FILES:
+   -----
+   If the magic field is "ni1", then the voxel data is stored in the
+   associated ".img" file, starting at offset 0 (i.e., vox_offset is not
+   used in this case, and should be set to 0.0).
+
+   When storing NIFTI-1 datasets in pairs of files, it is customary to name
+   the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5.
+   When storing in a single file ("n+1"), the file name should be in
+   the form "name.nii" (the ".nft" and ".nif" suffixes are already taken;
+   cf. http://www.icdatamaster.com/n.html ).
+
+   BYTE ORDERING:
+   -------------
+   The byte order of the data arrays is presumed to be the same as the byte
+   order of the header (which is determined by examining dim[0]).
+
+   Floating point types are presumed to be stored in IEEE-754 format.
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* DETAILS ABOUT vox_offset:
+   ------------------------
+   In a .nii file, the vox_offset field value is interpreted as the start
+   location of the image data bytes in that file. In a .hdr/.img file pair,
+   the vox_offset field value is the start location of the image data
+   bytes in the .img file.
+    * If vox_offset is less than 352 in a .nii file, it is equivalent
+      to 352 (i.e., image data never starts before byte #352 in a .nii file).
+    * The default value for vox_offset in a .nii file is 352.
+    * In a .hdr file, the default value for vox_offset is 0.
+    * vox_offset should be an integer multiple of 16; otherwise, some
+      programs may not work properly (e.g., SPM). This is to allow
+      memory-mapped input to be properly byte-aligned.
+   Note that since vox_offset is an IEEE-754 32 bit float (for compatibility
+   with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All
+   integers from 0 to 2^24 can be represented exactly in this format, but not
+   all larger integers are exactly storable as IEEE-754 32 bit floats. However,
+   unless you plan to have vox_offset be potentially larger than 16 MB, this
+   should not be an issue. (Actually, any integral multiple of 16 up to 2^27
+   can be represented exactly in this format, which allows for up to 128 MB
+   of random information before the image data.  If that isn't enough, then
+   perhaps this format isn't right for you.)
+
+   In a .img file (i.e., image data stored separately from the NIfTI-1
+   header), data bytes between #0 and #vox_offset-1 (inclusive) are completely
+   undefined and unregulated by the NIfTI-1 standard. One potential use of
+   having vox_offset > 0 in the .hdr/.img file pair storage method is to make
+   the .img file be a copy of (or link to) a pre-existing image file in some
+   other format, such as DICOM; then vox_offset would be set to the offset of
+   the image data in this file. (It may not be possible to follow the
+   "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1
+   format in such a case may lead to a file that is incompatible with software
+   that relies on vox_offset being a multiple of 16.)
+
+   In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may
+   be used to store user-defined extra information; similarly, in a .hdr file,
+   any data bytes after byte #347 are available for user-defined extra
+   information. The (very weak) regulation of this extra header data is
+   described elsewhere.
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* DATA SCALING:
+   ------------
+   If the scl_slope field is nonzero, then each voxel value in the dataset
+   should be scaled as
+      y = scl_slope * x + scl_inter
+   where x = voxel value stored
+         y = "true" voxel value
+   Normally, we would expect this scaling to be used to store "true" floating
+   values in a smaller integer datatype, but that is not required.  That is,
+   it is legal to use scaling even if the datatype is a float type (crazy,
+   perhaps, but legal).
+    - However, the scaling is to be ignored if datatype is DT_RGB24.
+    - If datatype is a complex type, then the scaling is to be
+      applied to both the real and imaginary parts.
+
+   The cal_min and cal_max fields (if nonzero) are used for mapping (possibly
+   scaled) dataset values to display colors:
+    - Minimum display intensity (black) corresponds to dataset value cal_min.
+    - Maximum display intensity (white) corresponds to dataset value cal_max.
+    - Dataset values below cal_min should display as black also, and values
+      above cal_max as white.
+    - Colors "black" and "white", of course, may refer to any scalar display
+      scheme (e.g., a color lookup table specified via aux_file).
+    - cal_min and cal_max only make sense when applied to scalar-valued
+      datasets (i.e., dim[0] < 5 or dim[5] = 1).
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* TYPE OF DATA (acceptable values for datatype field):
+   ---------------------------------------------------
+   Values of datatype smaller than 256 are ANALYZE 7.5 compatible.
+   Larger values are NIFTI-1 additions.  These are all multiples of 256, so
+   that no bits below position 8 are set in datatype.  But there is no need
+   to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do.
+
+   The additional codes are intended to include a complete list of basic
+   scalar types, including signed and unsigned integers from 8 to 64 bits,
+   floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits.
+
+   Note that most programs will support only a few of these datatypes!
+   A NIFTI-1 program should fail gracefully (e.g., print a warning message)
+   when it encounters a dataset with a type it doesn't like.
+-----------------------------------------------------------------------------*/
+
+//#undef DT_UNKNOWN  /* defined in dirent.h on some Unix systems */
+//https://www.mail-archive.com/fltk-bugs@easysw.com/msg05850.html
+/*! \defgroup NIFTI1_DATATYPES
+    \brief nifti1 datatype codes
+    @{
+ */
+                            /*--- the original ANALYZE 7.5 type codes ---*/
+#define DT_NONE                    0
+#define DT_UNKNOWN_DT              0  //3/3/2014 CR modified to avoid conflict /usr/include/dirent
+#define DT_BINARY                  1     /* binary (1 bit/voxel)         */
+#define DT_UNSIGNED_CHAR           2     /* unsigned char (8 bits/voxel) */
+#define DT_SIGNED_SHORT            4     /* signed short (16 bits/voxel) */
+#define DT_SIGNED_INT              8     /* signed int (32 bits/voxel)   */
+#define DT_FLOAT                  16     /* float (32 bits/voxel)        */
+#define DT_COMPLEX                32     /* complex (64 bits/voxel)      */
+#define DT_DOUBLE                 64     /* double (64 bits/voxel)       */
+#define DT_RGB                   128     /* RGB triple (24 bits/voxel)   */
+#define DT_ALL                   255     /* not very useful (?)          */
+
+                            /*----- another set of names for the same ---*/
+#define DT_UINT8                   2
+#define DT_INT16                   4
+#define DT_INT32                   8
+#define DT_FLOAT32                16
+#define DT_COMPLEX64              32
+#define DT_FLOAT64                64
+#define DT_RGB24                 128
+
+                            /*------------------- new codes for NIFTI ---*/
+#define DT_INT8                  256     /* signed char (8 bits)         */
+#define DT_UINT16                512     /* unsigned short (16 bits)     */
+#define DT_UINT32                768     /* unsigned int (32 bits)       */
+#define DT_INT64                1024     /* long long (64 bits)          */
+#define DT_UINT64               1280     /* unsigned long long (64 bits) */
+#define DT_FLOAT128             1536     /* long double (128 bits)       */
+#define DT_COMPLEX128           1792     /* double pair (128 bits)       */
+#define DT_COMPLEX256           2048     /* long double pair (256 bits)  */
+#define DT_RGBA32               2304     /* 4 byte RGBA (32 bits/voxel)  */
+/* @} */
+
+
+                            /*------- aliases for all the above codes ---*/
+
+/*! \defgroup NIFTI1_DATATYPE_ALIASES
+    \brief aliases for the nifti1 datatype codes
+    @{
+ */
+                                       /*! unsigned char. */
+#define NIFTI_TYPE_UINT8           2
+                                       /*! signed short. */
+#define NIFTI_TYPE_INT16           4
+                                       /*! signed int. */
+#define NIFTI_TYPE_INT32           8
+                                       /*! 32 bit float. */
+#define NIFTI_TYPE_FLOAT32        16
+                                       /*! 64 bit complex = 2 32 bit floats. */
+#define NIFTI_TYPE_COMPLEX64      32
+                                       /*! 64 bit float = double. */
+#define NIFTI_TYPE_FLOAT64        64
+                                       /*! 3 8 bit bytes. */
+#define NIFTI_TYPE_RGB24         128
+                                       /*! signed char. */
+#define NIFTI_TYPE_INT8          256
+                                       /*! unsigned short. */
+#define NIFTI_TYPE_UINT16        512
+                                       /*! unsigned int. */
+#define NIFTI_TYPE_UINT32        768
+                                       /*! signed long long. */
+#define NIFTI_TYPE_INT64        1024
+                                       /*! unsigned long long. */
+#define NIFTI_TYPE_UINT64       1280
+                                       /*! 128 bit float = long double. */
+#define NIFTI_TYPE_FLOAT128     1536
+                                       /*! 128 bit complex = 2 64 bit floats. */
+#define NIFTI_TYPE_COMPLEX128   1792
+                                       /*! 256 bit complex = 2 128 bit floats */
+#define NIFTI_TYPE_COMPLEX256   2048
+                                       /*! 4 8 bit bytes. */
+#define NIFTI_TYPE_RGBA32       2304
+/* @} */
+
+                     /*-------- sample typedefs for complicated types ---*/
+#if 0
+typedef struct { float       r,i;     } complex_float ;
+typedef struct { double      r,i;     } complex_double ;
+typedef struct { long double r,i;     } complex_longdouble ;
+typedef struct { unsigned char r,g,b; } rgb_byte ;
+#endif
+
+/*---------------------------------------------------------------------------*/
+/* INTERPRETATION OF VOXEL DATA:
+   ----------------------------
+   The intent_code field can be used to indicate that the voxel data has
+   some particular meaning.  In particular, a large number of codes is
+   given to indicate that the the voxel data should be interpreted as
+   being drawn from a given probability distribution.
+
+   VECTOR-VALUED DATASETS:
+   ----------------------
+   The 5th dimension of the dataset, if present (i.e., dim[0]=5 and
+   dim[5] > 1), contains multiple values (e.g., a vector) to be stored
+   at each spatiotemporal location.  For example, the header values
+    - dim[0] = 5
+    - dim[1] = 64
+    - dim[2] = 64
+    - dim[3] = 20
+    - dim[4] = 1     (indicates no time axis)
+    - dim[5] = 3
+    - datatype = DT_FLOAT
+    - intent_code = NIFTI_INTENT_VECTOR
+   mean that this dataset should be interpreted as a 3D volume (64x64x20),
+   with a 3-vector of floats defined at each point in the 3D grid.
+
+   A program reading a dataset with a 5th dimension may want to reformat
+   the image data to store each voxels' set of values together in a struct
+   or array.  This programming detail, however, is beyond the scope of the
+   NIFTI-1 file specification!  Uses of dimensions 6 and 7 are also not
+   specified here.
+
+   STATISTICAL PARAMETRIC DATASETS (i.e., SPMs):
+   --------------------------------------------
+   Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE
+   (inclusive) indicate that the numbers in the dataset should be interpreted
+   as being drawn from a given distribution.  Most such distributions have
+   auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter).
+
+   If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters
+   are the same for each voxel, and are given in header fields intent_p1,
+   intent_p2, and intent_p3.
+
+   If the dataset DOES have a 5th dimension, then the auxiliary parameters
+   are different for each voxel.  For example, the header values
+    - dim[0] = 5
+    - dim[1] = 128
+    - dim[2] = 128
+    - dim[3] = 1      (indicates a single slice)
+    - dim[4] = 1      (indicates no time axis)
+    - dim[5] = 2
+    - datatype = DT_FLOAT
+    - intent_code = NIFTI_INTENT_TTEST
+   mean that this is a 2D dataset (128x128) of t-statistics, with the
+   t-statistic being in the first "plane" of data and the degrees-of-freedom
+   parameter being in the second "plane" of data.
+
+   If the dataset 5th dimension is used to store the voxel-wise statistical
+   parameters, then dim[5] must be 1 plus the number of parameters required
+   by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5]
+   must be 2, as in the example just above).
+
+   Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is
+   why there is no code with value=1, which is obsolescent in AFNI).
+
+   OTHER INTENTIONS:
+   ----------------
+   The purpose of the intent_* fields is to help interpret the values
+   stored in the dataset.  Some non-statistical values for intent_code
+   and conventions are provided for storing other complex data types.
+
+   The intent_name field provides space for a 15 character (plus 0 byte)
+   'name' string for the type of data stored. Examples:
+    - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1";
+       could be used to signify that the voxel values are estimates of the
+       NMR parameter T1.
+    - intent_code = NIFTI_INTENT_TTEST; intent_name = "House";
+       could be used to signify that the voxel values are t-statistics
+       for the significance of 'activation' response to a House stimulus.
+    - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152";
+       could be used to signify that the voxel values are a displacement
+       vector that transforms each voxel (x,y,z) location to the
+       corresponding location in the MNI152 standard brain.
+    - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI";
+       could be used to signify that the voxel values comprise a diffusion
+       tensor image.
+
+   If no data name is implied or needed, intent_name[0] should be set to 0.
+-----------------------------------------------------------------------------*/
+
+ /*! default: no intention is indicated in the header. */
+
+#define NIFTI_INTENT_NONE        0
+
+    /*-------- These codes are for probability distributions ---------------*/
+    /* Most distributions have a number of parameters,
+       below denoted by p1, p2, and p3, and stored in
+        - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension
+        - image data array                if dataset does have 5th dimension
+
+       Functions to compute with many of the distributions below can be found
+       in the CDF library from U Texas.
+
+       Formulas for and discussions of these distributions can be found in the
+       following books:
+
+        [U] Univariate Discrete Distributions,
+            NL Johnson, S Kotz, AW Kemp.
+
+        [C1] Continuous Univariate Distributions, vol. 1,
+             NL Johnson, S Kotz, N Balakrishnan.
+
+        [C2] Continuous Univariate Distributions, vol. 2,
+             NL Johnson, S Kotz, N Balakrishnan.                            */
+    /*----------------------------------------------------------------------*/
+
+  /*! [C2, chap 32] Correlation coefficient R (1 param):
+       p1 = degrees of freedom
+       R/sqrt(1-R*R) is t-distributed with p1 DOF. */
+
+/*! \defgroup NIFTI1_INTENT_CODES
+    \brief nifti1 intent codes, to describe intended meaning of dataset contents
+    @{
+ */
+#define NIFTI_INTENT_CORREL      2
+
+  /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */
+
+#define NIFTI_INTENT_TTEST       3
+
+  /*! [C2, chap 27] Fisher F statistic (2 params):
+       p1 = numerator DOF, p2 = denominator DOF. */
+
+#define NIFTI_INTENT_FTEST       4
+
+  /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */
+
+#define NIFTI_INTENT_ZSCORE      5
+
+  /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF.
+      Density(x) proportional to exp(-x/2) * x^(p1/2-1). */
+
+#define NIFTI_INTENT_CHISQ       6
+
+  /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b.
+      Density(x) proportional to x^(a-1) * (1-x)^(b-1). */
+
+#define NIFTI_INTENT_BETA        7
+
+  /*! [U, chap 3] Binomial distribution (2 params):
+       p1 = number of trials, p2 = probability per trial.
+      Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */
+
+#define NIFTI_INTENT_BINOM       8
+
+  /*! [C1, chap 17] Gamma distribution (2 params):
+       p1 = shape, p2 = scale.
+      Density(x) proportional to x^(p1-1) * exp(-p2*x). */
+
+#define NIFTI_INTENT_GAMMA       9
+
+  /*! [U, chap 4] Poisson distribution (1 param): p1 = mean.
+      Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */
+
+#define NIFTI_INTENT_POISSON    10
+
+  /*! [C1, chap 13] Normal distribution (2 params):
+       p1 = mean, p2 = standard deviation. */
+
+#define NIFTI_INTENT_NORMAL     11
+
+  /*! [C2, chap 30] Noncentral F statistic (3 params):
+       p1 = numerator DOF, p2 = denominator DOF,
+       p3 = numerator noncentrality parameter.  */
+
+#define NIFTI_INTENT_FTEST_NONC 12
+
+  /*! [C2, chap 29] Noncentral chi-squared statistic (2 params):
+       p1 = DOF, p2 = noncentrality parameter.     */
+
+#define NIFTI_INTENT_CHISQ_NONC 13
+
+  /*! [C2, chap 23] Logistic distribution (2 params):
+       p1 = location, p2 = scale.
+      Density(x) proportional to sech^2((x-p1)/(2*p2)). */
+
+#define NIFTI_INTENT_LOGISTIC   14
+
+  /*! [C2, chap 24] Laplace distribution (2 params):
+       p1 = location, p2 = scale.
+      Density(x) proportional to exp(-abs(x-p1)/p2). */
+
+#define NIFTI_INTENT_LAPLACE    15
+
+  /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */
+
+#define NIFTI_INTENT_UNIFORM    16
+
+  /*! [C2, chap 31] Noncentral t statistic (2 params):
+       p1 = DOF, p2 = noncentrality parameter. */
+
+#define NIFTI_INTENT_TTEST_NONC 17
+
+  /*! [C1, chap 21] Weibull distribution (3 params):
+       p1 = location, p2 = scale, p3 = power.
+      Density(x) proportional to
+       ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */
+
+#define NIFTI_INTENT_WEIBULL    18
+
+  /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF.
+      Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0.
+       p1 = 1 = 'half normal' distribution
+       p1 = 2 = Rayleigh distribution
+       p1 = 3 = Maxwell-Boltzmann distribution.                  */
+
+#define NIFTI_INTENT_CHI        19
+
+  /*! [C1, chap 15] Inverse Gaussian (2 params):
+       p1 = mu, p2 = lambda
+      Density(x) proportional to
+       exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3  for x > 0. */
+
+#define NIFTI_INTENT_INVGAUSS   20
+
+  /*! [C2, chap 22] Extreme value type I (2 params):
+       p1 = location, p2 = scale
+      cdf(x) = exp(-exp(-(x-p1)/p2)). */
+
+#define NIFTI_INTENT_EXTVAL     21
+
+  /*! Data is a 'p-value' (no params). */
+
+#define NIFTI_INTENT_PVAL       22
+
+  /*! Data is ln(p-value) (no params).
+      To be safe, a program should compute p = exp(-abs(this_value)).
+      The nifti_stats.c library returns this_value
+      as positive, so that this_value = -log(p). */
+
+
+#define NIFTI_INTENT_LOGPVAL    23
+
+  /*! Data is log10(p-value) (no params).
+      To be safe, a program should compute p = pow(10.,-abs(this_value)).
+      The nifti_stats.c library returns this_value
+      as positive, so that this_value = -log10(p). */
+
+#define NIFTI_INTENT_LOG10PVAL  24
+
+  /*! Smallest intent_code that indicates a statistic. */
+
+#define NIFTI_FIRST_STATCODE     2
+
+  /*! Largest intent_code that indicates a statistic. */
+
+#define NIFTI_LAST_STATCODE     24
+
+ /*---------- these values for intent_code aren't for statistics ----------*/
+
+ /*! To signify that the value at each voxel is an estimate
+     of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE.
+     The name of the parameter may be stored in intent_name.     */
+
+#define NIFTI_INTENT_ESTIMATE  1001
+
+ /*! To signify that the value at each voxel is an index into
+     some set of labels, set intent_code = NIFTI_INTENT_LABEL.
+     The filename with the labels may stored in aux_file.        */
+
+#define NIFTI_INTENT_LABEL     1002
+
+ /*! To signify that the value at each voxel is an index into the
+     NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */
+
+#define NIFTI_INTENT_NEURONAME 1003
+
+ /*! To store an M x N matrix at each voxel:
+       - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1)
+       - intent_code must be NIFTI_INTENT_GENMATRIX
+       - dim[5] must be M*N
+       - intent_p1 must be M (in float format)
+       - intent_p2 must be N (ditto)
+       - the matrix values A[i][[j] are stored in row-order:
+         - A[0][0] A[0][1] ... A[0][N-1]
+         - A[1][0] A[1][1] ... A[1][N-1]
+         - etc., until
+         - A[M-1][0] A[M-1][1] ... A[M-1][N-1]        */
+
+#define NIFTI_INTENT_GENMATRIX 1004
+
+ /*! To store an NxN symmetric matrix at each voxel:
+       - dataset must have a 5th dimension
+       - intent_code must be NIFTI_INTENT_SYMMATRIX
+       - dim[5] must be N*(N+1)/2
+       - intent_p1 must be N (in float format)
+       - the matrix values A[i][[j] are stored in row-order:
+         - A[0][0]
+         - A[1][0] A[1][1]
+         - A[2][0] A[2][1] A[2][2]
+         - etc.: row-by-row                           */
+
+#define NIFTI_INTENT_SYMMATRIX 1005
+
+ /*! To signify that the vector value at each voxel is to be taken
+     as a displacement field or vector:
+       - dataset must have a 5th dimension
+       - intent_code must be NIFTI_INTENT_DISPVECT
+       - dim[5] must be the dimensionality of the displacment
+         vector (e.g., 3 for spatial displacement, 2 for in-plane) */
+
+#define NIFTI_INTENT_DISPVECT  1006   /* specifically for displacements */
+#define NIFTI_INTENT_VECTOR    1007   /* for any other type of vector */
+
+ /*! To signify that the vector value at each voxel is really a
+     spatial coordinate (e.g., the vertices or nodes of a surface mesh):
+       - dataset must have a 5th dimension
+       - intent_code must be NIFTI_INTENT_POINTSET
+       - dim[0] = 5
+       - dim[1] = number of points
+       - dim[2] = dim[3] = dim[4] = 1
+       - dim[5] must be the dimensionality of space (e.g., 3 => 3D space).
+       - intent_name may describe the object these points come from
+         (e.g., "pial", "gray/white" , "EEG", "MEG").                   */
+
+#define NIFTI_INTENT_POINTSET  1008
+
+ /*! To signify that the vector value at each voxel is really a triple
+     of indexes (e.g., forming a triangle) from a pointset dataset:
+       - dataset must have a 5th dimension
+       - intent_code must be NIFTI_INTENT_TRIANGLE
+       - dim[0] = 5
+       - dim[1] = number of triangles
+       - dim[2] = dim[3] = dim[4] = 1
+       - dim[5] = 3
+       - datatype should be an integer type (preferably DT_INT32)
+       - the data values are indexes (0,1,...) into a pointset dataset. */
+
+#define NIFTI_INTENT_TRIANGLE  1009
+
+ /*! To signify that the vector value at each voxel is a quaternion:
+       - dataset must have a 5th dimension
+       - intent_code must be NIFTI_INTENT_QUATERNION
+       - dim[0] = 5
+       - dim[5] = 4
+       - datatype should be a floating point type     */
+
+#define NIFTI_INTENT_QUATERNION 1010
+
+ /*! Dimensionless value - no params - although, as in _ESTIMATE 
+     the name of the parameter may be stored in intent_name.     */
+
+#define NIFTI_INTENT_DIMLESS    1011
+
+ /*---------- these values apply to GIFTI datasets ----------*/
+
+ /*! To signify that the value at each location is from a time series. */
+
+#define NIFTI_INTENT_TIME_SERIES  2001
+
+ /*! To signify that the value at each location is a node index, from
+     a complete surface dataset.                                       */
+
+#define NIFTI_INTENT_NODE_INDEX   2002
+
+ /*! To signify that the vector value at each location is an RGB triplet,
+     of whatever type.
+       - dataset must have a 5th dimension
+       - dim[0] = 5
+       - dim[1] = number of nodes
+       - dim[2] = dim[3] = dim[4] = 1
+       - dim[5] = 3
+    */
+
+#define NIFTI_INTENT_RGB_VECTOR   2003
+
+ /*! To signify that the vector value at each location is a 4 valued RGBA
+     vector, of whatever type.
+       - dataset must have a 5th dimension
+       - dim[0] = 5
+       - dim[1] = number of nodes
+       - dim[2] = dim[3] = dim[4] = 1
+       - dim[5] = 4
+    */
+
+#define NIFTI_INTENT_RGBA_VECTOR  2004
+
+ /*! To signify that the value at each location is a shape value, such
+     as the curvature.  */
+
+#define NIFTI_INTENT_SHAPE        2005
+
+/* @} */
+
+/*---------------------------------------------------------------------------*/
+/* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE:
+   ---------------------------------------------------
+   There are 3 different methods by which continuous coordinates can
+   attached to voxels.  The discussion below emphasizes 3D volumes, and
+   the continuous coordinates are referred to as (x,y,z).  The voxel
+   index coordinates (i.e., the array indexes) are referred to as (i,j,k),
+   with valid ranges:
+     i = 0 .. dim[1]-1
+     j = 0 .. dim[2]-1  (if dim[0] >= 2)
+     k = 0 .. dim[3]-1  (if dim[0] >= 3)
+   The (x,y,z) coordinates refer to the CENTER of a voxel.  In methods
+   2 and 3, the (x,y,z) axes refer to a subject-based coordinate system,
+   with
+     +x = Right  +y = Anterior  +z = Superior.
+   This is a right-handed coordinate system.  However, the exact direction
+   these axes point with respect to the subject depends on qform_code
+   (Method 2) and sform_code (Method 3).
+
+   N.B.: The i index varies most rapidly, j index next, k index slowest.
+    Thus, voxel (i,j,k) is stored starting at location
+      (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8)
+    into the dataset array.
+
+   N.B.: The ANALYZE 7.5 coordinate system is
+      +x = Left  +y = Anterior  +z = Superior
+    which is a left-handed coordinate system.  This backwardness is
+    too difficult to tolerate, so this NIFTI-1 standard specifies the
+    coordinate order which is most common in functional neuroimaging.
+
+   N.B.: The 3 methods below all give the locations of the voxel centers
+    in the (x,y,z) coordinate system.  In many cases, programs will wish
+    to display image data on some other grid.  In such a case, the program
+    will need to convert its desired (x,y,z) values into (i,j,k) values
+    in order to extract (or interpolate) the image data.  This operation
+    would be done with the inverse transformation to those described below.
+
+   N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is
+    stored in the otherwise unused pixdim[0].  If pixdim[0]=0.0 (which
+    should not occur), we take qfac=1.  Of course, pixdim[0] is only used
+    when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header.
+
+   N.B.: The units of (x,y,z) can be specified using the xyzt_units field.
+
+   METHOD 1 (the "old" way, used only when qform_code = 0):
+   -------------------------------------------------------
+   The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE
+   7.5 way.  This is a simple scaling relationship:
+
+     x = pixdim[1] * i
+     y = pixdim[2] * j
+     z = pixdim[3] * k
+
+   No particular spatial orientation is attached to these (x,y,z)
+   coordinates.  (NIFTI-1 does not have the ANALYZE 7.5 orient field,
+   which is not general and is often not set properly.)  This method
+   is not recommended, and is present mainly for compatibility with
+   ANALYZE 7.5 files.
+
+   METHOD 2 (used when qform_code > 0, which should be the "normal" case):
+   ---------------------------------------------------------------------
+   The (x,y,z) coordinates are given by the pixdim[] scales, a rotation
+   matrix, and a shift.  This method is intended to represent
+   "scanner-anatomical" coordinates, which are often embedded in the
+   image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030),
+   and (0018,0050)), and represent the nominal orientation and location of
+   the data.  This method can also be used to represent "aligned"
+   coordinates, which would typically result from some post-acquisition
+   alignment of the volume to a standard orientation (e.g., the same
+   subject on another day, or a rigid rotation to true anatomical
+   orientation from the tilted position of the subject in the scanner).
+   The formula for (x,y,z) in terms of header parameters and (i,j,k) is:
+
+     [ x ]   [ R11 R12 R13 ] [        pixdim[1] * i ]   [ qoffset_x ]
+     [ y ] = [ R21 R22 R23 ] [        pixdim[2] * j ] + [ qoffset_y ]
+     [ z ]   [ R31 R32 R33 ] [ qfac * pixdim[3] * k ]   [ qoffset_z ]
+
+   The qoffset_* shifts are in the NIFTI-1 header.  Note that the center
+   of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is
+   just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z).
+
+   The rotation matrix R is calculated from the quatern_* parameters.
+   This calculation is described below.
+
+   The scaling factor qfac is either 1 or -1.  The rotation matrix R
+   defined by the quaternion parameters is "proper" (has determinant 1).
+   This may not fit the needs of the data; for example, if the image
+   grid is
+     i increases from Left-to-Right
+     j increases from Anterior-to-Posterior
+     k increases from Inferior-to-Superior
+   Then (i,j,k) is a left-handed triple.  In this example, if qfac=1,
+   the R matrix would have to be
+
+     [  1   0   0 ]
+     [  0  -1   0 ]  which is "improper" (determinant = -1).
+     [  0   0   1 ]
+
+   If we set qfac=-1, then the R matrix would be
+
+     [  1   0   0 ]
+     [  0  -1   0 ]  which is proper.
+     [  0   0  -1 ]
+
+   This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0]
+   (which encodes a 180 degree rotation about the x-axis).
+
+   METHOD 3 (used when sform_code > 0):
+   -----------------------------------
+   The (x,y,z) coordinates are given by a general affine transformation
+   of the (i,j,k) indexes:
+
+     x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3]
+     y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3]
+     z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3]
+
+   The srow_* vectors are in the NIFTI_1 header.  Note that no use is
+   made of pixdim[] in this method.
+
+   WHY 3 METHODS?
+   --------------
+   Method 1 is provided only for backwards compatibility.  The intention
+   is that Method 2 (qform_code > 0) represents the nominal voxel locations
+   as reported by the scanner, or as rotated to some fiducial orientation and
+   location.  Method 3, if present (sform_code > 0), is to be used to give
+   the location of the voxels in some standard space.  The sform_code
+   indicates which standard space is present.  Both methods 2 and 3 can be
+   present, and be useful in different contexts (method 2 for displaying the
+   data on its original grid; method 3 for displaying it on a standard grid).
+
+   In this scheme, a dataset would originally be set up so that the
+   Method 2 coordinates represent what the scanner reported.  Later,
+   a registration to some standard space can be computed and inserted
+   in the header.  Image display software can use either transform,
+   depending on its purposes and needs.
+
+   In Method 2, the origin of coordinates would generally be whatever
+   the scanner origin is; for example, in MRI, (0,0,0) is the center
+   of the gradient coil.
+
+   In Method 3, the origin of coordinates would depend on the value
+   of sform_code; for example, for the Talairach coordinate system,
+   (0,0,0) corresponds to the Anterior Commissure.
+
+   QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2)
+   -------------------------------------------------------
+   The orientation of the (x,y,z) axes relative to the (i,j,k) axes
+   in 3D space is specified using a unit quaternion [a,b,c,d], where
+   a*a+b*b+c*c+d*d=1.  The (b,c,d) values are all that is needed, since
+   we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative.  The (b,c,d)
+   values are stored in the (quatern_b,quatern_c,quatern_d) fields.
+
+   The quaternion representation is chosen for its compactness in
+   representing rotations. The (proper) 3x3 rotation matrix that
+   corresponds to [a,b,c,d] is
+
+         [ a*a+b*b-c*c-d*d   2*b*c-2*a*d       2*b*d+2*a*c     ]
+     R = [ 2*b*c+2*a*d       a*a+c*c-b*b-d*d   2*c*d-2*a*b     ]
+         [ 2*b*d-2*a*c       2*c*d+2*a*b       a*a+d*d-c*c-b*b ]
+
+         [ R11               R12               R13             ]
+       = [ R21               R22               R23             ]
+         [ R31               R32               R33             ]
+
+   If (p,q,r) is a unit 3-vector, then rotation of angle h about that
+   direction is represented by the quaternion
+
+     [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)].
+
+   Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi.  (Note that
+   [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2
+   quaternions that can be used to represent a given rotation matrix R.)
+   To rotate a 3-vector (x,y,z) using quaternions, we compute the
+   quaternion product
+
+     [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d]
+
+   which is equivalent to the matrix-vector multiply
+
+     [ x' ]     [ x ]
+     [ y' ] = R [ y ]   (equivalence depends on a*a+b*b+c*c+d*d=1)
+     [ z' ]     [ z ]
+
+   Multiplication of 2 quaternions is defined by the following:
+
+     [a,b,c,d] = a*1 + b*I + c*J + d*K
+     where
+       I*I = J*J = K*K = -1 (I,J,K are square roots of -1)
+       I*J =  K    J*K =  I    K*I =  J
+       J*I = -K    K*J = -I    I*K = -J  (not commutative!)
+     For example
+       [a,b,0,0] * [0,0,0,1] = [0,0,-b,a]
+     since this expands to
+       (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J).
+
+   The above formula shows how to go from quaternion (b,c,d) to
+   rotation matrix and direction cosines.  Conversely, given R,
+   we can compute the fields for the NIFTI-1 header by
+
+     a = 0.5  * sqrt(1+R11+R22+R33)    (not stored)
+     b = 0.25 * (R32-R23) / a       => quatern_b
+     c = 0.25 * (R13-R31) / a       => quatern_c
+     d = 0.25 * (R21-R12) / a       => quatern_d
+
+   If a=0 (a 180 degree rotation), alternative formulas are needed.
+   See the nifti1_io.c function mat44_to_quatern() for an implementation
+   of the various cases in converting R to [a,b,c,d].
+
+   Note that R-transpose (= R-inverse) would lead to the quaternion
+   [a,-b,-c,-d].
+
+   The choice to specify the qoffset_x (etc.) values in the final
+   coordinate system is partly to make it easy to convert DICOM images to
+   this format.  The DICOM attribute "Image Position (Patient)" (0020,0032)
+   stores the (Xd,Yd,Zd) coordinates of the center of the first voxel.
+   Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z,
+   where (x,y,z) refers to the NIFTI coordinate system discussed above.
+   (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior,
+        whereas +x is Right, +y is Anterior  , +z is Superior. )
+   Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then
+     qoffset_x = -px   qoffset_y = -py   qoffset_z = pz
+   is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT.
+
+   That is, DICOM's coordinate system is 180 degrees rotated about the z-axis
+   from the neuroscience/NIFTI coordinate system.  To transform between DICOM
+   and NIFTI, you just have to negate the x- and y-coordinates.
+
+   The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the
+   orientation of the x- and y-axes of the image data in terms of 2 3-vectors.
+   The first vector is a unit vector along the x-axis, and the second is
+   along the y-axis.  If the (0020,0037) attribute is extracted into the
+   value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix
+   would be
+              [ -xa  -ya ]
+              [ -xb  -yb ]
+              [  xc   yc ]
+   The negations are because DICOM's x- and y-axes are reversed relative
+   to NIFTI's.  The third column of the R matrix gives the direction of
+   displacement (relative to the subject) along the slice-wise direction.
+   This orientation is not encoded in the DICOM standard in a simple way;
+   DICOM is mostly concerned with 2D images.  The third column of R will be
+   either the cross-product of the first 2 columns or its negative.  It is
+   possible to infer the sign of the 3rd column by examining the coordinates
+   in DICOM attribute (0020,0032) "Image Position (Patient)" for successive
+   slices.  However, this method occasionally fails for reasons that I
+   (RW Cox) do not understand.
+-----------------------------------------------------------------------------*/
+
+   /* [qs]form_code value:  */      /* x,y,z coordinate system refers to:    */
+   /*-----------------------*/      /*---------------------------------------*/
+
+/*! \defgroup NIFTI1_XFORM_CODES
+    \brief nifti1 xform codes to describe the "standard" coordinate system
+    @{
+ */
+                                    /*! Arbitrary coordinates (Method 1). */
+
+#define NIFTI_XFORM_UNKNOWN      0
+
+                                    /*! Scanner-based anatomical coordinates */
+
+#define NIFTI_XFORM_SCANNER_ANAT 1
+
+                                    /*! Coordinates aligned to another file's,
+                                        or to anatomical "truth".            */
+
+#define NIFTI_XFORM_ALIGNED_ANAT 2
+
+                                    /*! Coordinates aligned to Talairach-
+                                        Tournoux Atlas; (0,0,0)=AC, etc. */
+
+#define NIFTI_XFORM_TALAIRACH    3
+
+                                    /*! MNI 152 normalized coordinates. */
+
+#define NIFTI_XFORM_MNI_152      4
+/* @} */
+
+/*---------------------------------------------------------------------------*/
+/* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS:
+   ----------------------------------------
+   The codes below can be used in xyzt_units to indicate the units of pixdim.
+   As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for
+   time (t).
+    - If dim[4]=1 or dim[0] < 4, there is no time axis.
+    - A single time series (no space) would be specified with
+      - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data)
+      - dim[1] = dim[2] = dim[3] = 1
+      - dim[4] = number of time points
+      - pixdim[4] = time step
+      - xyzt_units indicates units of pixdim[4]
+      - dim[5] = number of values stored at each time point
+
+   Bits 0..2 of xyzt_units specify the units of pixdim[1..3]
+    (e.g., spatial units are values 1..7).
+   Bits 3..5 of xyzt_units specify the units of pixdim[4]
+    (e.g., temporal units are multiples of 8).
+
+   This compression of 2 distinct concepts into 1 byte is due to the
+   limited space available in the 348 byte ANALYZE 7.5 header.  The
+   macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the
+   undesired bits from the xyzt_units fields, leaving "pure" space
+   and time codes.  Inversely, the macro SPACE_TIME_TO_XYZT can be
+   used to assemble a space code (0,1,2,...,7) with a time code
+   (0,8,16,32,...,56) into the combined value for xyzt_units.
+
+   Note that codes are provided to indicate the "time" axis units are
+   actually frequency in Hertz (_HZ), in part-per-million (_PPM)
+   or in radians-per-second (_RADS).
+
+   The toffset field can be used to indicate a nonzero start point for
+   the time axis.  That is, time point #m is at t=toffset+m*pixdim[4]
+   for m=0..dim[4]-1.
+-----------------------------------------------------------------------------*/
+
+/*! \defgroup NIFTI1_UNITS
+    \brief nifti1 units codes to describe the unit of measurement for
+           each dimension of the dataset
+    @{
+ */
+                               /*! NIFTI code for unspecified units. */
+#define NIFTI_UNITS_UNKNOWN 0
+
+                               /** Space codes are multiples of 1. **/
+                               /*! NIFTI code for meters. */
+#define NIFTI_UNITS_METER   1
+                               /*! NIFTI code for millimeters. */
+#define NIFTI_UNITS_MM      2
+                               /*! NIFTI code for micrometers. */
+#define NIFTI_UNITS_MICRON  3
+
+                               /** Time codes are multiples of 8. **/
+                               /*! NIFTI code for seconds. */
+#define NIFTI_UNITS_SEC     8
+                               /*! NIFTI code for milliseconds. */
+#define NIFTI_UNITS_MSEC   16
+                               /*! NIFTI code for microseconds. */
+#define NIFTI_UNITS_USEC   24
+
+                               /*** These units are for spectral data: ***/
+                               /*! NIFTI code for Hertz. */
+#define NIFTI_UNITS_HZ     32
+                               /*! NIFTI code for ppm. */
+#define NIFTI_UNITS_PPM    40
+                               /*! NIFTI code for radians per second. */
+#define NIFTI_UNITS_RADS   48
+/* @} */
+
+#undef  XYZT_TO_SPACE
+#undef  XYZT_TO_TIME
+#define XYZT_TO_SPACE(xyzt)       ( (xyzt) & 0x07 )
+#define XYZT_TO_TIME(xyzt)        ( (xyzt) & 0x38 )
+
+#undef  SPACE_TIME_TO_XYZT
+#define SPACE_TIME_TO_XYZT(ss,tt) (  (((char)(ss)) & 0x07)   \
+                                   | (((char)(tt)) & 0x38) )
+
+/*---------------------------------------------------------------------------*/
+/* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION:
+   ---------------------------------------------
+   A few fields are provided to store some extra information
+   that is sometimes important when storing the image data
+   from an FMRI time series experiment.  (After processing such
+   data into statistical images, these fields are not likely
+   to be useful.)
+
+  { freq_dim  } = These fields encode which spatial dimension (1,2, or 3)
+  { phase_dim } = corresponds to which acquisition dimension for MRI data.
+  { slice_dim } =
+    Examples:
+      Rectangular scan multi-slice EPI:
+        freq_dim = 1  phase_dim = 2  slice_dim = 3  (or some permutation)
+      Spiral scan multi-slice EPI:
+        freq_dim = phase_dim = 0  slice_dim = 3
+        since the concepts of frequency- and phase-encoding directions
+        don't apply to spiral scan
+
+    slice_duration = If this is positive, AND if slice_dim is nonzero,
+                     indicates the amount of time used to acquire 1 slice.
+                     slice_duration*dim[slice_dim] can be less than pixdim[4]
+                     with a clustered acquisition method, for example.
+
+    slice_code = If this is nonzero, AND if slice_dim is nonzero, AND
+                 if slice_duration is positive, indicates the timing
+                 pattern of the slice acquisition.  The following codes
+                 are defined:
+                   NIFTI_SLICE_SEQ_INC  == sequential increasing
+                   NIFTI_SLICE_SEQ_DEC  == sequential decreasing
+                   NIFTI_SLICE_ALT_INC  == alternating increasing
+                   NIFTI_SLICE_ALT_DEC  == alternating decreasing
+                   NIFTI_SLICE_ALT_INC2 == alternating increasing #2
+                   NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2
+  { slice_start } = Indicates the start and end of the slice acquisition
+  { slice_end   } = pattern, when slice_code is nonzero.  These values
+                    are present to allow for the possible addition of
+                    "padded" slices at either end of the volume, which
+                    don't fit into the slice timing pattern.  If there
+                    are no padding slices, then slice_start=0 and
+                    slice_end=dim[slice_dim]-1 are the correct values.
+                    For these values to be meaningful, slice_start must
+                    be non-negative and slice_end must be greater than
+                    slice_start.  Otherwise, they should be ignored.
+
+  The following table indicates the slice timing pattern, relative to
+  time=0 for the first slice acquired, for some sample cases.  Here,
+  dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1,
+  and slice_start=1, slice_end=5 (1 padded slice on each end).
+
+  slice
+  index  SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2
+    6  :   n/a     n/a     n/a     n/a    n/a      n/a    n/a = not applicable
+    5  :   0.4     0.0     0.2     0.0    0.4      0.2    (slice time offset
+    4  :   0.3     0.1     0.4     0.3    0.1      0.0     doesn't apply to
+    3  :   0.2     0.2     0.1     0.1    0.3      0.3     slices outside
+    2  :   0.1     0.3     0.3     0.4    0.0      0.1     the range
+    1  :   0.0     0.4     0.0     0.2    0.2      0.4     slice_start ..
+    0  :   n/a     n/a     n/a     n/a    n/a      n/a     slice_end)
+
+  The SEQ slice_codes are sequential ordering (uncommon but not unknown),
+  either increasing in slice number or decreasing (INC or DEC), as
+  illustrated above.
+
+  The ALT slice codes are alternating ordering.  The 'standard' way for
+  these to operate (without the '2' on the end) is for the slice timing
+  to start at the edge of the slice_start .. slice_end group (at slice_start
+  for INC and at slice_end for DEC).  For the 'ALT_*2' slice_codes, the
+  slice timing instead starts at the first slice in from the edge (at
+  slice_start+1 for INC2 and at slice_end-1 for DEC2).  This latter
+  acquisition scheme is found on some Siemens scanners.
+
+  The fields freq_dim, phase_dim, slice_dim are all squished into the single
+  byte field dim_info (2 bits each, since the values for each field are
+  limited to the range 0..3).  This unpleasantness is due to lack of space
+  in the 348 byte allowance.
+
+  The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and
+  DIM_INFO_TO_SLICE_DIM can be used to extract these values from the
+  dim_info byte.
+
+  The macro FPS_INTO_DIM_INFO can be used to put these 3 values
+  into the dim_info byte.
+-----------------------------------------------------------------------------*/
+
+#undef  DIM_INFO_TO_FREQ_DIM
+#undef  DIM_INFO_TO_PHASE_DIM
+#undef  DIM_INFO_TO_SLICE_DIM
+
+#define DIM_INFO_TO_FREQ_DIM(di)   ( ((di)     ) & 0x03 )
+#define DIM_INFO_TO_PHASE_DIM(di)  ( ((di) >> 2) & 0x03 )
+#define DIM_INFO_TO_SLICE_DIM(di)  ( ((di) >> 4) & 0x03 )
+
+#undef  FPS_INTO_DIM_INFO
+#define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03)      ) |  \
+                                      ( ( ((char)(pd)) & 0x03) << 2 ) |  \
+                                      ( ( ((char)(sd)) & 0x03) << 4 )  )
+
+/*! \defgroup NIFTI1_SLICE_ORDER
+    \brief nifti1 slice order codes, describing the acquisition order
+           of the slices
+    @{
+ */
+#define NIFTI_SLICE_UNKNOWN   0
+#define NIFTI_SLICE_SEQ_INC   1
+#define NIFTI_SLICE_SEQ_DEC   2
+#define NIFTI_SLICE_ALT_INC   3
+#define NIFTI_SLICE_ALT_DEC   4
+#define NIFTI_SLICE_ALT_INC2  5  /* 05 May 2005: RWCox */
+#define NIFTI_SLICE_ALT_DEC2  6  /* 05 May 2005: RWCox */
+/* @} */
+
+/*---------------------------------------------------------------------------*/
+/* UNUSED FIELDS:
+   -------------
+   Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set
+   to particular values for compatibility with other programs.  The issue
+   of interoperability of ANALYZE 7.5 files is a murky one -- not all
+   programs require exactly the same set of fields.  (Unobscuring this
+   murkiness is a principal motivation behind NIFTI-1.)
+
+   Some of the fields that may need to be set for other (non-NIFTI aware)
+   software to be happy are:
+
+     extents    dbh.h says this should be 16384
+     regular    dbh.h says this should be the character 'r'
+     glmin,   } dbh.h says these values should be the min and max voxel
+      glmax   }  values for the entire dataset
+
+   It is best to initialize ALL fields in the NIFTI-1 header to 0
+   (e.g., with calloc()), then fill in what is needed.
+-----------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* MISCELLANEOUS C MACROS
+-----------------------------------------------------------------------------*/
+
+/*.................*/
+/*! Given a nifti_1_header struct, check if it has a good magic number.
+    Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */
+
+#define NIFTI_VERSION(h)                               \
+ ( ( (h).magic[0]=='n' && (h).magic[3]=='\0'    &&     \
+     ( (h).magic[1]=='i' || (h).magic[1]=='+' ) &&     \
+     ( (h).magic[2]>='1' && (h).magic[2]<='9' )   )    \
+ ? (h).magic[2]-'0' : 0 )
+
+/*.................*/
+/*! Check if a nifti_1_header struct says if the data is stored in the
+    same file or in a separate file.  Returns 1 if the data is in the same
+    file as the header, 0 if it is not.                                   */
+
+#define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' )
+
+/*.................*/
+/*! Check if a nifti_1_header struct needs to be byte swapped.
+    Returns 1 if it needs to be swapped, 0 if it does not.     */
+
+#define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 )
+
+/*.................*/
+/*! Check if a nifti_1_header struct contains a 5th (vector) dimension.
+    Returns size of 5th dimension if > 1, returns 0 otherwise.         */
+
+#define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 )
+
+/*****************************************************************************/
+
+/*=================*/
+#ifdef  __cplusplus
+}
+#endif
+/*=================*/
+
+#endif /* _NIFTI_HEADER_ */
diff --git a/console/nifti1_io_core.cpp b/console/nifti1_io_core.cpp
new file mode 100644
index 0000000..73c3add
--- /dev/null
+++ b/console/nifti1_io_core.cpp
@@ -0,0 +1,479 @@
+#include "nifti1_io_core.h"
+#include <math.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+#include <stdio.h>
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
+void nifti_swap_8bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+7;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 8;
+    }
+    return ;
+}
+
+void nifti_swap_4bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+3;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 4;
+    }
+    return ;
+}
+
+void nifti_swap_2bytes( size_t n , void *ar )    // 2 bytes at a time
+{
+    size_t ii ;
+    unsigned char * cp1 = (unsigned char *)ar, * cp2 ;
+    unsigned char   tval;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp2 = cp1 + 1;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1 += 2;
+    }
+    return ;
+}
+
+int isSameFloat (float a, float b) {
+    return (fabs (a - b) <= FLT_EPSILON);
+}
+
+ivec3 setiVec3(int x, int y, int z)
+{
+    ivec3 v = {x, y, z};
+    return v;
+}
+
+vec3 setVec3(float x, float y, float z)
+{
+    vec3 v = {x, y, z};
+    return v;
+}
+
+vec4 setVec4(float x, float y, float z)
+{
+    vec4 v= {x, y, z, 1};
+    return v;
+}
+
+vec3 crossProduct(vec3 u, vec3 v)
+{
+    return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2],
+                   -u.v[0]*v.v[2] + v.v[0]*u.v[2],
+                   u.v[0]*v.v[1] - v.v[0]*u.v[1]);
+}
+
+float dotProduct(vec3 u, vec3 v)
+{
+    return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]);
+}
+
+vec3 nifti_vect33_norm (vec3 v) { //normalize vector length
+    vec3 vO = v;
+    float vLen = sqrt( (v.v[0]*v.v[0])
+                      + (v.v[1]*v.v[1])
+                      + (v.v[2]*v.v[2]));
+    if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero
+    for (int i = 0; i < 3; i++)
+        vO.v[i] = v.v[i]/vLen;
+    return vO;
+}
+
+vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix
+    vec3 vO;
+    for (int i=0; i<3; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<3; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix
+    vec4 vO;
+    for (int i=0; i<4; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<4; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) {
+    //create NIfTI header based on values from DICOM header
+    //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1
+    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] );
+    /* 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 ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ;
+    } else {
+        Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ;
+    }
+    /* normalize row 2 */
+    val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ;
+    if( val > 0.0l ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ;
+    } else {
+        Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ;
+    }
+    /* row 3 is the cross product of rows 1 and 2*/
+    Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ;  /* cross */
+    Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ;  /* product */
+    Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ;
+    Q = nifti_mat33_transpose(Q);
+    if (nifti_mat33_determ(Q) < 0.0) {
+        Q.m[0][2] = -Q.m[0][2];
+        Q.m[1][2] = -Q.m[1][2];
+        Q.m[2][2] = -Q.m[2][2];
+    }
+    //next scale matrix
+    LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]);
+    Q = nifti_mat33_mul(Q,diagVox);
+    mat44 Q44; //4x4 matrix includes translations
+    LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1],
+               Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2],
+               Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]);
+    return Q44;
+}
+
+float nifti_mat33_determ( mat33 R )   /* determinant of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 ;
+    /*  INPUT MATRIX:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  /* [ r11 r12 r13 ] */
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  /* [ r21 r22 r23 ] */
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  /* [ r31 r32 r33 ] */
+    return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33
+                   +r21*r32*r13+r31*r12*r23-r31*r22*r13) ;
+}
+
+mat33 nifti_mat33_mul( mat33 A , mat33 B )  /* multiply 2 3x3 matrices */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 C ; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j] ;
+    return C ;
+}
+
+mat44 nifti_mat44_mul( mat44 A , mat44 B )  /* multiply 2 3x3 matrices */
+{
+    mat44 C ; int i,j ;
+    for( i=0 ; i < 4 ; i++ )
+        for( j=0 ; j < 4; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j]
+            + A.m[i][3] * B.m[3][j];
+    return C ;
+}
+
+mat33 nifti_mat33_transpose( mat33 A )  /* transpose 3x3 matrix */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 B; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            B.m[i][j] =  A.m[j][i];
+    return B;
+}
+
+mat33 nifti_mat33_inverse( mat33 R )   /* inverse of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ;
+    mat33 Q ;
+    //  INPUT MATRIX:
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    return Q ;
+}
+
+float nifti_mat33_rownorm( mat33 A )  // max row norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ;
+    r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ;
+    r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+float nifti_mat33_colnorm( mat33 A )  // max column norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ;
+    r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ;
+    r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+mat33 nifti_mat33_polar( mat33 A )
+{
+    mat33 X , Y , Z ;
+    float alp,bet,gam,gmi , dif=1.0 ;
+    int k=0 ;
+    X = A ;
+    // force matrix to be nonsingular
+    gam = nifti_mat33_determ(X) ;
+    while( gam == 0.0 ){        // perturb matrix
+        gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ;
+        X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ;
+        gam = nifti_mat33_determ(X) ;
+    }
+    while(1){
+        Y = nifti_mat33_inverse(X) ;
+        if( dif > 0.3 ){     // far from convergence
+            alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ;
+            bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ;
+            gam = sqrt( bet / alp ) ;
+            gmi = 1.0 / gam ;
+        } else
+            gam = gmi = 1.0 ;  // close to convergence
+        Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ;
+        Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ;
+        Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ;
+        Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ;
+        Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ;
+        Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ;
+        Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ;
+        Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ;
+        Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ;
+        dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1])
+        +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0])
+        +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2])
+        +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1])
+        +fabs(Z.m[2][2]-X.m[2][2])                          ;
+        k = k+1 ;
+        if( k > 100 || dif < 3.e-6 ) break ;  // convergence or exhaustion
+        X = Z ;
+    }
+    return Z ;
+}
+
+void nifti_mat44_to_quatern( mat44 R ,
+                            float *qb, float *qc, float *qd,
+                            float *qx, float *qy, float *qz,
+                            float *dx, float *dy, float *dz, float *qfac )
+{
+    double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ;
+    double xd,yd,zd , a,b,c,d ;
+    mat33 P,Q ;
+    // offset outputs are read write out of input matrix
+    ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ;
+    // load 3x3 matrix into local variables */
+    r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ;
+    r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ;
+    r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ;
+    // compute lengths of each column; these determine grid spacings
+    xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ;
+    yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ;
+    zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ;
+    // if a column length is zero, patch the trouble
+    if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; }
+    if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; }
+    if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; }
+    // assign the output lengths */
+    ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ;
+    // normalize the columns */
+    r11 /= xd ; r21 /= xd ; r31 /= xd ;
+    r12 /= yd ; r22 /= yd ; r32 /= yd ;
+    r13 /= zd ; r23 /= zd ; r33 /= zd ;
+    /* At this point, the matrix has normal columns, but we have to allow
+     for the fact that the hideous user may not have given us a matrix
+     with orthogonal columns.
+     So, now find the orthogonal matrix closest to the current matrix.
+     One reason for using the polar decomposition to get this
+     orthogonal matrix, rather than just directly orthogonalizing
+     the columns, is so that inputting the inverse matrix to R
+     will result in the inverse orthogonal matrix at this point.
+     If we just orthogonalized the columns, this wouldn't necessarily hold. */
+    Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q
+    Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ;
+    Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ;
+    P = nifti_mat33_polar(Q) ;  // P is orthog matrix closest to Q
+    r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload
+    r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ;
+    r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ;
+    //                            [ r11 r12 r13 ]
+    // at this point, the matrix  [ r21 r22 r23 ] is orthogonal
+    //                            [ r31 r32 r33 ]
+    // compute the determinant to determine if it is proper
+    zd = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;  // should be -1 or 1
+    if( zd > 0 ){             // proper
+        ASSIF(qfac,1.0) ;
+    } else {                  // improper ==> flip 3rd column
+        ASSIF(qfac,-1.0) ;
+        r13 = -r13 ; r23 = -r23 ; r33 = -r33 ;
+    }
+    // now, compute quaternion parameters
+    a = r11 + r22 + r33 + 1.0l ;
+    if( a > 0.5l ){                // simplest case
+        a = 0.5l * sqrt(a) ;
+        b = 0.25l * (r32-r23) / a ;
+        c = 0.25l * (r13-r31) / a ;
+        d = 0.25l * (r21-r12) / a ;
+    } else {                       // trickier case
+        xd = 1.0 + r11 - (r22+r33) ;  // 4*b*b
+        yd = 1.0 + r22 - (r11+r33) ;  // 4*c*c
+        zd = 1.0 + r33 - (r11+r22) ;  // 4*d*d
+        if( xd > 1.0 ){
+            b = 0.5l * sqrt(xd) ;
+            c = 0.25l* (r12+r21) / b ;
+            d = 0.25l* (r13+r31) / b ;
+            a = 0.25l* (r32-r23) / b ;
+        } else if( yd > 1.0 ){
+            c = 0.5l * sqrt(yd) ;
+            b = 0.25l* (r12+r21) / c ;
+            d = 0.25l* (r23+r32) / c ;
+            a = 0.25l* (r13-r31) / c ;
+        } else {
+            d = 0.5l * sqrt(zd) ;
+            b = 0.25l* (r13+r31) / d ;
+            c = 0.25l* (r23+r32) / d ;
+            a = 0.25l* (r21-r12) / d ;
+        }
+        //        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; }
+        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded...
+    }
+    ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ;
+    return ;
+}
+
+mat44 nifti_quatern_to_mat44( float qb, float qc, float qd,
+                             float qx, float qy, float qz,
+                             float dx, float dy, float dz, float qfac )
+{
+    mat44 R ;
+    double a,b=qb,c=qc,d=qd , xd,yd,zd ;
+
+    /* last row is always [ 0 0 0 1 ] */
+
+    R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ;
+
+    /* compute a parameter from b,c,d */
+
+    a = 1.0l - (b*b + c*c + d*d) ;
+    if( a < 1.e-7l ){                   /* special case */
+        a = 1.0l / sqrt(b*b+c*c+d*d) ;
+        b *= a ; c *= a ; d *= a ;        /* normalize (b,c,d) vector */
+        a = 0.0l ;                        /* a = 0 ==> 180 degree rotation */
+    } else{
+        a = sqrt(a) ;                     /* angle = 2*arccos(a) */
+    }
+
+    /* load rotation matrix, including scaling factors for voxel sizes */
+
+    xd = (dx > 0.0) ? dx : 1.0l ;       /* make sure are positive */
+    yd = (dy > 0.0) ? dy : 1.0l ;
+    zd = (dz > 0.0) ? dz : 1.0l ;
+
+    if( qfac < 0.0 ) zd = -zd ;         /* left handedness? */
+
+    R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ;
+    R.m[0][1] = 2.0l * (b*c-a*d        ) * yd ;
+    R.m[0][2] = 2.0l * (b*d+a*c        ) * zd ;
+    R.m[1][0] = 2.0l * (b*c+a*d        ) * xd ;
+    R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ;
+    R.m[1][2] = 2.0l * (c*d-a*b        ) * zd ;
+    R.m[2][0] = 2.0l * (b*d-a*c        ) * xd ;
+    R.m[2][1] = 2.0l * (c*d+a*b        ) * yd ;
+    R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ;
+
+    /* load offsets */
+
+    R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ;
+
+    return R ;
+}
+
+mat44 nifti_mat44_inverse( mat44 R )
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ;
+    mat44 Q ;
+    /*  INPUT MATRIX IS:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 v1 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 v2 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 v3 ]
+    v1  = R.m[0][3]; v2  = R.m[1][3]; v3  = R.m[2][3];  // [  0   0   0   1 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3
+                      -r22*v1*r33-r32*r13*v2+r32*v1*r23) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3
+                      +r21*v1*r33+r31*r13*v2-r31*v1*r23) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3
+                      -r21*r32*v1-r31*r12*v2+r31*r22*v1) ;
+    Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ;
+    Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0
+    return Q ;
+}
+
+
+
+
+
+
+
+
+
diff --git a/console/nifti1_io_core.h b/console/nifti1_io_core.h
new file mode 100644
index 0000000..41f4615
--- /dev/null
+++ b/console/nifti1_io_core.h
@@ -0,0 +1,74 @@
+//this minimal set of nifti routines is based on nifti1_io with the dependencies (zlib) and a few extra functions
+// http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.h
+// http://niftilib.sourceforge.net
+#ifndef _NIFTI_IO_CORE_HEADER_
+#define _NIFTI_IO_CORE_HEADER_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <string.h>
+
+typedef struct {                   /** 4x4 matrix struct **/
+    float m[3][3] ;
+} mat33 ;
+typedef struct {                   /** 4x4 matrix struct **/
+    float m[4][4] ;
+} mat44 ;
+typedef struct {                   /** x4 vector struct **/
+    float v[4] ;
+} vec4 ;
+typedef struct {                   /** x3 vector struct **/
+    float v[3] ;
+} vec3 ;
+typedef struct {                   /** x4 vector struct INTEGER**/
+    int v[3] ;
+} ivec3 ;
+
+#define LOAD_MAT33(AA,a11,a12,a13 ,a21,a22,a23 ,a31,a32,a33)    \
+( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 ,   \
+AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23  ,   \
+AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33            )
+
+#define LOAD_MAT44(AA,a11,a12,a13,a14,a21,a22,a23,a24,a31,a32,a33,a34)    \
+( AA.m[0][0]=a11 , AA.m[0][1]=a12 , AA.m[0][2]=a13 , AA.m[0][3]=a14 ,   \
+AA.m[1][0]=a21 , AA.m[1][1]=a22 , AA.m[1][2]=a23 , AA.m[1][3]=a24 ,   \
+AA.m[2][0]=a31 , AA.m[2][1]=a32 , AA.m[2][2]=a33 , AA.m[2][3]=a34 ,   \
+AA.m[3][0]=AA.m[3][1]=AA.m[3][2]=0.0f , AA.m[3][3]=1.0f            )
+
+#undef  ASSIF  // assign v to *p, if possible
+#define ASSIF(p,v) if( (p)!=NULL ) *(p) = (v)
+float dotProduct(vec3 u, vec3 v);
+float nifti_mat33_determ( mat33 R ) ;
+int isSameFloat (float a, float b) ;
+mat33 nifti_mat33_inverse( mat33 R );
+mat33 nifti_mat33_mul( mat33 A , mat33 B );
+mat33 nifti_mat33_transpose( mat33 A ) ;
+mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]);
+mat44 nifti_mat44_inverse( mat44 R );
+mat44 nifti_mat44_mul( mat44 A , mat44 B );
+vec3 crossProduct(vec3 u, vec3 v);
+vec3 nifti_vect33_norm (vec3 v);
+vec3 nifti_vect33mat33_mul(vec3 v, mat33 m );
+ivec3 setiVec3(int x, int y, int z);
+vec3 setVec3(float x, float y, float z);
+vec4 setVec4(float x, float y, float z);
+vec4 nifti_vect44mat44_mul(vec4 v, mat44 m );
+void nifti_swap_2bytes( size_t n , void *ar );    // 2 bytes at a time
+void nifti_swap_4bytes( size_t n , void *ar );    // 4 bytes at a time
+void nifti_swap_8bytes( size_t n , void *ar );    // 8 bytes at a time
+void nifti_mat44_to_quatern( mat44 R ,
+                            float *qb, float *qc, float *qd,
+                            float *qx, float *qy, float *qz,
+                            float *dx, float *dy, float *dz, float *qfac );
+mat44 nifti_quatern_to_mat44( float qb, float qc, float qd,
+                             float qx, float qy, float qz,
+                             float dx, float dy, float dz, float qfac );
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp
new file mode 100644
index 0000000..94c74d9
--- /dev/null
+++ b/console/nii_dicom.cpp
@@ -0,0 +1,3273 @@
+//#define MY_DEBUG
+#if defined(_WIN64) || defined(_WIN32)
+	#include <windows.h> //write to registry
+#endif
+#ifdef _MSC_VER
+	#include <direct.h>
+	#define getcwd _getcwd
+	#define chdir _chrdir
+	#include "io.h"
+	#include <math.h>
+	//#define snprintf _snprintf
+	//#define vsnprintf _vsnprintf
+	#define strcasecmp _stricmp
+	#define strncasecmp _strnicmp
+#else
+	#include <unistd.h>
+#endif
+//#include <time.h> //clock()
+#include "nifti1.h"
+#include "nii_dicom.h"
+#include <sys/types.h>
+#include <sys/stat.h> // discriminate files from folders
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h> //toupper
+#include <math.h>
+#include <string.h>
+#include <stddef.h>
+#include "jpg_0XC3.h"
+#include <float.h>
+#include <stdint.h>
+#include "nifti1_io_core.h"
+#include "ujpeg.h"
+#ifdef myUseCOut
+#include <iostream>
+#endif
+#ifdef myEnableJasper
+    #include <jasper/jasper.h>
+#endif
+#ifndef myDisableOpenJPEG
+    #include <openjpeg-2.1/openjpeg.h>//"openjpeg.h"
+
+#ifdef myEnableJasper
+ERROR: YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY
+#endif
+
+unsigned char * imagetoimg(opj_image_t * image)
+{
+    int numcmpts = image->numcomps;
+    int sgnd = image->comps[0].sgnd ;
+    int width = image->comps[0].w;
+    int height = image->comps[0].h;
+    int bpp = (image->comps[0].prec + 7) >> 3; //e.g. 12 bits requires 2 bytes
+    int imgbytes = bpp * width * height * numcmpts;
+    bool isOK = true;
+    if (numcmpts > 1) {
+        for (int comp = 1; comp < numcmpts; comp++) { //check RGB data
+            if (image->comps[0].w != image->comps[comp].w) isOK = false;
+            if (image->comps[0].h != image->comps[comp].h) isOK = false;
+            if (image->comps[0].dx != image->comps[comp].dx) isOK = false;
+            if (image->comps[0].dy != image->comps[comp].dy) isOK = false;
+            if (image->comps[0].prec != image->comps[comp].prec) isOK = false;
+            if (image->comps[0].sgnd != image->comps[comp].sgnd) isOK = false;
+        }
+        if (numcmpts != 3) isOK = false; //we only handle Gray and RedGreenBlue, not GrayAlpha or RedGreenBlueAlpha
+        if (image->comps[0].prec != 8) isOK = false; //only 8-bit for RGB data
+    }
+    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());
+        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());
+    #endif
+    //extract the data
+    if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) {
+        printf("Serious catastrophic decompression error\n");
+        return NULL;
+    }
+    unsigned char *img = (unsigned char *)malloc(imgbytes);
+    uint16_t * img16ui = (uint16_t*) img; //unsigned 16-bit
+    int16_t * img16i = (int16_t*) img; //signed 16-bit
+    if (sgnd) bpp = -bpp;
+    if (bpp == -1) {
+        free(img);
+        printf("Error: Signed 8-bit DICOM?\n");
+        return NULL;
+    }
+    //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
+    int pix = 0; //ouput pixel
+    for (int cmptno = 0; cmptno < numcmpts; ++cmptno) {
+        int cpix = 0; //component pixel
+        int* v = image->comps[cmptno].data;
+        for (int y = 0; y < height; ++y) {
+            for (int x = 0; x < width; ++x) {
+                switch (bpp) {
+                    case 1:
+                        img[pix] = (unsigned char) v[cpix];
+                        break;
+                    case 2:
+                        img16ui[pix] = (uint16_t) v[cpix];
+                        break;
+                    case -2:
+                        img16i[pix] = (int16_t) v[cpix];
+                        break;
+                }
+                pix ++;
+                cpix ++;
+            }//for x
+        } //for y
+    } //for each component
+    return img;
+}// imagetoimg()
+
+typedef struct bufinfo {
+    unsigned char *buf;
+    unsigned char *cur;
+    size_t len;
+} BufInfo;
+
+static void my_stream_free (void * p_user_data) { //do nothing
+    //BufInfo d = (BufInfo) p_user_data;
+    //free(d.buf);
+} // my_stream_free()
+
+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;
+    }
+    else
+    {
+        l_nb_read = (OPJ_UINT32)(p_file->buf + p_file->len - p_file->cur);
+    }
+    memcpy(p_buffer, p_file->cur, l_nb_read);
+    p_file->cur += l_nb_read;
+
+    return l_nb_read ? l_nb_read : ((OPJ_UINT32)-1);
+} //opj_read_from_buffer()
+
+static OPJ_UINT32 opj_write_from_buffer(void * p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo* p_file) {
+    memcpy(p_file->cur,p_buffer, p_nb_bytes);
+    p_file->cur += p_nb_bytes;
+    p_file->len += p_nb_bytes;
+    return p_nb_bytes;
+} // opj_write_from_buffer()
+
+static OPJ_SIZE_T opj_skip_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo * p_file) {
+    if(p_file->cur + p_nb_bytes < p_file->buf + p_file->len )
+    {
+        p_file->cur += p_nb_bytes;
+        return p_nb_bytes;
+    }
+    p_file->cur = p_file->buf + p_file->len;
+    return (OPJ_SIZE_T)-1;
+} //opj_skip_from_buffer()
+
+static OPJ_BOOL opj_seek_from_buffer(OPJ_SIZE_T p_nb_bytes, BufInfo * p_file) {
+    if(p_file->cur + p_nb_bytes < p_file->buf + p_file->len ) {
+        p_file->cur += p_nb_bytes;
+        return OPJ_TRUE;
+    }
+    p_file->cur = p_file->buf + p_file->len;
+    return OPJ_FALSE;
+} //opj_seek_from_buffer()
+
+opj_stream_t* opj_stream_create_buffer_stream(BufInfo* p_file, OPJ_UINT32 p_size, OPJ_BOOL p_is_read_stream) {
+    opj_stream_t* l_stream;
+    if(! p_file) return NULL;
+    l_stream = opj_stream_create(p_size, p_is_read_stream);
+    if(! l_stream) return NULL;
+    opj_stream_set_user_data(l_stream, p_file , my_stream_free);
+    opj_stream_set_user_data_length(l_stream, p_file->len);
+    opj_stream_set_read_function(l_stream,  (opj_stream_read_fn) opj_read_from_buffer);
+    opj_stream_set_write_function(l_stream, (opj_stream_write_fn) opj_write_from_buffer);
+    opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn) opj_skip_from_buffer);
+    opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn) opj_seek_from_buffer);
+    return l_stream;
+} //opj_stream_create_buffer_stream()
+
+unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) {
+    //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
+    unsigned char * ret = NULL;
+    opj_dparameters_t params;
+    opj_codec_t *codec;
+    opj_image_t *jpx;
+    opj_stream_t *stream;
+    FILE *reader = fopen(imgname, "rb");
+    fseek(reader, 0, SEEK_END);
+    long size = ftell(reader)- dcm.imageStart;
+    if (size <= 8) return NULL;
+    fseek(reader, dcm.imageStart, 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", 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);
+        goto cleanup1;
+    }
+    ret = imagetoimg(jpx);
+cleanup1:
+    opj_image_destroy(jpx);
+cleanup2:
+    free(dx.buf);
+    opj_stream_destroy(stream);
+    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
+#define M_PI           3.14159265358979323846
+#endif
+
+#ifdef MY_DEBUG
+float deFuzz(float v) {
+    if (fabs(v) < 0.00001)
+        return 0;
+    else
+        return v;
+
+}
+
+void reportMat33(char *str, mat33 A) {
+    printf("%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]));
+}
+
+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,
+           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]));
+}
+#endif
+
+int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R){
+    //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped
+    if (h->dim[3] < 2) return 0; //don't care direction for single slice
+    int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column
+    if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; //
+    if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition
+    float pos = NAN;
+    if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist
+        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);
+#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);
+#endif
+    }
+    if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) )
+        pos = d.stackOffcentre[iSL];
+    if (isnan(pos) && ( !isnan(d.lastScanLoc)) )
+        pos = d.lastScanLoc;
+    vec4 x;
+    x.v[0] = 0.0; x.v[1] = 0.0; x.v[2]=(float)(h->dim[3]-1.0); x.v[3] = 1.0;
+    vec4 pos1v = nifti_vect44mat44_mul(x, *R);
+    float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0
+    bool flip = false;
+    if (!isnan(pos)) // we have real SliceLocation for last slice or volume center
+        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
+    }
+    if (flip) {
+        for (int i = 0; i < 4; i++)
+            R->m[i][2] = -R->m[i][2];
+    }
+    if (flip)
+        iSL = -iSL;
+	#ifdef MY_DEBUG
+    printf("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);
+	#endif
+	return iSL;
+} //verify_slice_dir()
+
+mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
+{
+    mat44 ret = Q44;
+    bool isNaN44 = false;
+    for (int i = 0; i < 4; i++)
+        for (int j = 0; j < 4; j++)
+            if (isnan(ret.m[i][j]))
+                isNaN44 = true;
+    if (isNaN44) {
+        printf("Warning: 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)
+                    ret.m[i][j] = 1;
+                else
+                    ret.m[i][j] = 0;
+        ret.m[1][1] = -1;
+    } //if isNaN detected
+    return ret;
+}
+
+void setQSForm(struct nifti_1_header *h, mat44 Q44i) {
+    mat44 Q44 = noNaN(Q44i);
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->srow_x[0] = Q44.m[0][0];
+    h->srow_x[1] = Q44.m[0][1];
+    h->srow_x[2] = Q44.m[0][2];
+    h->srow_x[3] = Q44.m[0][3];
+    h->srow_y[0] = Q44.m[1][0];
+    h->srow_y[1] = Q44.m[1][1];
+    h->srow_y[2] = Q44.m[1][2];
+    h->srow_y[3] = Q44.m[1][3];
+    h->srow_z[0] = Q44.m[2][0];
+    h->srow_z[1] = Q44.m[2][1];
+    h->srow_z[2] = Q44.m[2][2];
+    h->srow_z[3] = Q44.m[2][3];
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    h->qform_code = NIFTI_XFORM_SCANNER_ANAT;
+} //setQSForm()
+
+#ifdef my_unused
+
+ivec3 maxCol(mat33 R) {
+//return index of maximum column in 3x3 matrix, e.g. [1 0 0; 0 1 0; 0 0 1] -> 1,2,3
+	ivec3 ixyz;
+	//foo is abs(R)
+    mat33 foo;
+    for (int i=0 ; i < 3 ; i++ )
+        for (int j=0 ; j < 3 ; j++ )
+            foo.m[i][j] =  fabs(R.m[i][j]);
+	//ixyz.v[0] : row with largest value in column 1
+	ixyz.v[0] = 1;
+	if ((foo.m[1][0] > foo.m[0][0]) && (foo.m[1][0] >= foo.m[2][0]))
+		ixyz.v[0] = 2; //2nd column largest column
+	else if ((foo.m[2][0] > foo.m[0][0]) && (foo.m[2][0] > foo.m[1][0]))
+		ixyz.v[0] = 3; //3rd column largest column
+	//ixyz.v[1] : row with largest value in column 2, but not the same row as ixyz.v[1]
+	if (ixyz.v[0] == 1) {
+		ixyz.v[1] = 2;
+		if (foo.m[2][1] > foo.m[1][1])
+			ixyz.v[1] = 3;
+	} else if (ixyz.v[0] == 2) {
+		ixyz.v[1] = 1;
+		if (foo.m[2][1] > foo.m[0][1])
+			ixyz.v[1] = 3;
+	} else { //ixyz.v[0] == 3
+		ixyz.v[1] = 1;
+		if (foo.m[1][1] > foo.m[0][1])
+			ixyz.v[1] = 2;
+	}
+	//ixyz.v[2] : 3rd row, constrained by previous rows
+	ixyz.v[2] = 6 - ixyz.v[1] - ixyz.v[0];//sum of 1+2+3
+	return ixyz;
+}
+
+int sign(float x) {
+//returns -1,0,1 depending on if X is less than, equal to or greater than zero
+	if (x < 0)
+		return -1;
+	else if (x > 0)
+		return 1;
+	return 0;
+}
+
+// Subfunction: get dicom xform matrix and related info
+// This is a direct port of  Xiangrui Li's dicm2nii function
+mat44 xform_mat(struct TDICOMdata d) {
+	vec3 readV = setVec3(d.orient[1],d.orient[2],d.orient[3]);
+	vec3 phaseV = setVec3(d.orient[4],d.orient[5],d.orient[6]);
+    vec3 sliceV = crossProduct(readV ,phaseV);
+    mat33 R;
+    LOAD_MAT33(R, readV.v[0], readV.v[1], readV.v[2],
+    	phaseV.v[0], phaseV.v[1], phaseV.v[2],
+    	sliceV.v[0], sliceV.v[1], sliceV.v[2]);
+    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]);
+	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);
+	//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]);
+	mat33 pixdim;
+    LOAD_MAT33(pixdim, d.xyzMM[1], 0.0, 0.0,
+    	0.0, d.xyzMM[2], 0.0,
+    	0.0, 0.0, d.xyzMM[3]);
+	R = nifti_mat33_mul(R, pixdim);
+	//reportMat33((char*)"R",R);
+	mat44 R44;
+	LOAD_MAT44(R44, R.m[0][0], R.m[0][1], R.m[0][2], d.patientPosition[1],
+		R.m[1][0], R.m[1][1], R.m[1][2], d.patientPosition[2],
+		R.m[2][0], R.m[2][1], R.m[2][2], d.patientPosition[3]);
+	//reportMat44((char*)"R",R44);
+	//rest are former: R = verify_slice_dir(R, s, dim, iSL)
+
+
+	if ((d.xyzDim[3]<2) && (d.CSA.mosaicSlices < 2))
+		return R44; //don't care direction for single slice
+	vec3 dim = setVec3(d.xyzDim[1], d.xyzDim[2], d.xyzDim[3]);
+	if (d.CSA.mosaicSlices > 1) { //Siemens mosaic: use dim(1) since no transpose to img
+        float nRowCol = ceil(sqrt((double) d.CSA.mosaicSlices));
+        dim.v[0] = dim.v[0] / nRowCol;
+        dim.v[1] = dim.v[1] / nRowCol;
+        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);
+		R44.m[0][3] = offset.v[0];
+		R44.m[1][3] = offset.v[1];
+		R44.m[2][3] = offset.v[2];
+		//R44.m[3][3] = offset.v[3];
+		if (sign(d.CSA.sliceNormV[iSL]) != sign(cosSL)) {
+			R44.m[0][2] = -R44.m[0][2];
+			R44.m[1][2] = -R44.m[1][2];
+			R44.m[2][2] = -R44.m[2][2];
+			R44.m[3][2] = -R44.m[3][2];
+		}
+        //reportMat44((char*)"iR44",R44);
+		return R44;
+	} else if (true) {
+//SliceNormalVector TO DO
+		printf("Not completed");
+		exit(2);
+		return R44;
+	}
+	printf("Unable to determine spatial transform\n");
+	exit(1);
+}
+
+
+mat44 set_nii_header(struct TDICOMdata d) {
+	mat44 R = xform_mat(d);
+	//R(1:2,:) = -R(1:2,:); % dicom LPS to nifti RAS, xform matrix before reorient
+    for (int i=0; i<2; i++)
+        for(int j=0; j<4; j++)
+            R.m[i][j] = -R.m[i][j];
+	#ifdef MY_DEBUG
+    reportMat44((char*)"R44",R);
+	#endif
+}
+#endif
+
+// 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;
+    mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM);
+    if (d.CSA.mosaicSlices > 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;
+        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]);
+        for (int c=0; c<2; c++)
+            for (int r=0; r<4; r++)
+                Q44.m[c][r] = -Q44.m[c][r];
+        mat33 Q;
+        LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1],
+                   d.orient[2],d.orient[5],d.CSA.sliceNormV[2],
+                   d.orient[3],d.orient[6],d.CSA.sliceNormV[3]);
+        if  (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this.
+            mat44 det;
+            *sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly
+            LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l);
+            //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices;
+            Q44 = nifti_mat44_mul(Q44,det);
+        }
+    } else { //not a mosaic
+        *sliceDir = verify_slice_dir(d, d2, h, &Q44);
+        for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient
+            for (int r=0; r<2; r++) //swap rows 1 & 2
+                Q44.m[r][c] = - Q44.m[r][c];
+    }
+	#ifdef MY_DEBUG
+    reportMat44((char*)"Q44",Q44);
+	#endif
+    return Q44;
+}
+
+int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_1_header *h) { //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
+    h->sform_code = NIFTI_XFORM_UNKNOWN;
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    bool isOK = false;
+    for (int i = 1; i <= 6; i++)
+        if (d.orient[i] != 0.0) isOK = true;
+    if (!isOK) {
+        //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");
+    }
+    //mat44 Q44 = set_nii_header(d);
+    mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir);
+    setQSForm(h,Q44);
+    return sliceDir;
+} //headerDcm2NiiSForm()
+
+int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //final pass after de-mosaic
+    char txt[1024] = {""};
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder;
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29
+    sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime);
+    if (d.CSA.phaseEncodingDirectionPositive >= 0) {
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";phase=%d", d.CSA.phaseEncodingDirectionPositive);
+        strcat(txt,dtxt);
+    }
+    if ((d.CSA.bandwidthPerPixelPhaseEncode > 0) && ((d.phaseEncodingRC =='C') || (d.phaseEncodingRC =='R'))) {
+        float dwellTime = 0;
+        if (d.phaseEncodingRC =='C')
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+        else
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";dwell=%.3f", dwellTime);
+        strcat(txt,dtxt);
+    }
+    //from dicm2nii 20151117 InPlanePhaseEncodingDirection
+    if (d.phaseEncodingRC =='R')
+        h->dim_info = (3 << 4) + (1 << 2) + 2;
+    if (d.phaseEncodingRC =='C')
+        h->dim_info = (3 << 4) + (2 << 2) + 1;
+    snprintf(h->descrip,80, "%s",txt);
+    if (strlen(d.imageComments) > 0)
+        snprintf(h->aux_file,24,"%s",d.imageComments);
+    return headerDcm2NiiSForm(d,d2, h);
+} //headerDcm2Nii2()
+
+int dcmStrLen (int len) {
+    if (len < kDICOMStr)
+        return len+1;
+    else
+        return kDICOMStr;
+} //dcmStrLen()
+
+struct TDICOMdata clear_dicom_data() {
+    struct TDICOMdata d;
+    //d.dti4D = NULL;
+    d.locationsInAcquisition = 0;
+    for (int i=0; i < 4; i++) {
+            d.CSA.dtiV[i] = 0;
+        d.patientPosition[i] = NAN;
+        //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D
+        d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D
+        d.stackOffcentre[i] = NAN;
+        d.angulation[i] = 0.0f;
+        d.xyzMM[i] = 1;
+    }
+    d.CSA.numDti = 0;
+    for (int i=0; i < 5; i++)
+        d.xyzDim[i] = 1;
+    for (int i = 0; i < 7; i++)
+        d.orient[i] = 0.0f;
+    d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false;
+    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.flipAngle = 0.0;
+    d.fieldStrength = 0.0;
+    d.numberOfDynamicScans = 0;
+    d.echoNum = 1;
+    d.coilNum = 1;
+    d.imageBytes = 0;
+    d.intenScale = 1;
+    d.intenIntercept = 0;
+    d.gantryTilt = 0.0;
+    d.seriesNum = 1;
+    d.acquNum = 0;
+    d.imageNum = 1;
+    d.imageStart = 0;
+    d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE
+    d.isNonImage = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP
+    d.bitsAllocated = 16;//bits
+    d.bitsStored = 0;
+    d.samplesPerPixel = 1;
+    d.isValid = 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
+    d.compressionScheme = 0; //none
+    d.isExplicitVR = true;
+    d.isLittleEndian = true; //DICOM initially always little endian
+    d.converted2NII = 0;
+    d.phaseEncodingRC = '?';
+    d.CSA.bandwidthPerPixelPhaseEncode = 0.0;
+    d.CSA.mosaicSlices = 0;
+    d.CSA.sliceNormV[1] = 1.0;
+    d.CSA.sliceNormV[2] = 0.0;
+    d.CSA.sliceNormV[3] = 0.0;
+    d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN;
+    d.CSA.slice_start = 0;
+    d.CSA.slice_end = 0;
+    d.CSA.protocolSliceNumber1 = 0;
+    d.CSA.phaseEncodingDirectionPositive = -1; //unknown
+    d.CSA.isPhaseMap = false;
+    d.CSA.multiBandFactor = 1;
+    return d;
+} //clear_dicom_data()
+
+void dcmStrDigitsOnly(char* lStr) {
+    //e.g. change "H11" to " 11"
+    size_t len = strlen(lStr);
+    if (len < 1) return;
+    for (int i = 0; i < (int) len; i++)
+        if (!isdigit(lStr[i]) )
+            lStr[i] = ' ';
+
+}
+
+void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
+    //char test[] = " 1     2    3    ";
+    //lLength = (int)strlen(test);
+
+    if (lLength < 1) return;
+#ifdef _MSC_VER
+	char * cString = (char *)malloc(sizeof(char) * (lLength + 1));
+#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]);
+    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) {
+            unsigned char c = (unsigned char)cString[i];
+            if ((c >= 192) && (c <= 198)) cString[i] = 'A';
+            if (c == 199) cString[i] = 'C';
+            if ((c >= 200) && (c <= 203)) cString[i] = 'E';
+            if ((c >= 204) && (c <= 207)) cString[i] = 'I';
+            if (c == 208) cString[i] = 'D';
+            if (c == 209) cString[i] = 'N';
+            if ((c >= 210) && (c <= 214)) cString[i] = 'O';
+            if (c == 215) cString[i] = 'x';
+            if (c == 216) cString[i] = 'O';
+            if ((c >= 217) && (c <= 220)) cString[i] = 'O';
+            if (c == 221) cString[i] = 'Y';
+            if ((c >= 224) && (c <= 230)) cString[i] = 'a';
+            if (c == 231) cString[i] = 'c';
+            if ((c >= 232) && (c <= 235)) cString[i] = 'e';
+            if ((c >= 236) && (c <= 239)) cString[i] = 'i';
+            if (c == 240) cString[i] = 'o';
+            if (c == 241) cString[i] = 'n';
+            if ((c >= 242) && (c <= 246)) cString[i] = 'o';
+            if (c == 248) cString[i] = 'o';
+            if ((c >= 249) && (c <= 252)) cString[i] = 'u';
+            if (c == 253) cString[i] = 'y';
+            if (c == 255) cString[i] = 'y';
+        }
+    for (int i = 0; i < lLength; i++)
+        if ((cString[i]<1) || (cString[i]==' ') || (cString[i]==',') || (cString[i]=='^') || (cString[i]=='/') || (cString[i]=='\\')  || (cString[i]=='%') || (cString[i]=='*')) cString[i] = '_';
+    int len = 1;
+    for (int i = 1; i < lLength; i++) { //remove repeated "_"
+        if ((cString[i-1]!='_') || (cString[i]!='_')) {
+            cString[len] =cString[i];
+            len++;
+        }
+    } //for each item
+    if (cString[len-1] == '_') len--;
+    //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
+    cString[len] = 0; //null-terminate, strlcpy does this anyway
+    len = dcmStrLen(len);
+    memcpy(lOut,cString,len-1);
+    lOut[len-1] = 0;
+#ifdef _MSC_VER
+	free(cString);
+#endif
+} //dcmStr()
+
+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;
+    memcpy(&retVal, (char*)&lBuffer[0], 4);
+    if (!swap) return retVal;
+    float swapVal;
+    char *inFloat = ( char* ) & retVal;
+    char *outFloat = ( char* ) & swapVal;
+    outFloat[0] = inFloat[3];
+    outFloat[1] = inFloat[2];
+    outFloat[2] = inFloat[1];
+    outFloat[3] = inFloat[0];
+    //printf("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
+    double retVal = 0.0f;
+    if (lByteLength < 8) return retVal;
+    memcpy(&retVal, (char*)&lBuffer[0], 8);
+    if (!swap) return retVal;
+    char *floatToConvert = ( char* ) & lBuffer;
+    char *returnFloat = ( char* ) & retVal;
+    //swap the bytes into a temporary buffer
+    returnFloat[0] = floatToConvert[7];
+    returnFloat[1] = floatToConvert[6];
+    returnFloat[2] = floatToConvert[5];
+    returnFloat[3] = floatToConvert[4];
+    returnFloat[4] = floatToConvert[3];
+    returnFloat[5] = floatToConvert[2];
+    returnFloat[6] = floatToConvert[1];
+    returnFloat[7] = floatToConvert[0];
+    //printf("swapped val = %f\n",retVal);
+    return retVal;
+} //dcmFloatDouble()
+
+int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer
+    if (littleEndian) {
+        if (lByteLength <= 3)
+            return  lBuffer[0] | (lBuffer[1]<<8); //shortint vs word?
+        return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word?
+    }
+    if (lByteLength <= 3)
+        return  lBuffer[1] | (lBuffer[0]<<8); //shortint vs word?
+    return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word?
+} //dcmInt()
+
+int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
+#ifdef _MSC_VER
+	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
+#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]);
+    int ret = atoi(cString);
+#ifdef _MSC_VER
+	free(cString);
+#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
+	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
+#else
+	char cString[lByteLength + 1];
+#endif
+	int ret = kMANUFACTURER_UNKNOWN;
+    cString[lByteLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    //printf("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'))
+        ret = kMANUFACTURER_GE;
+    if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H'))
+        ret = kMANUFACTURER_PHILIPS;
+    if ((toupper(cString[0])== 'T') && (toupper(cString[1])== 'O'))
+        ret = kMANUFACTURER_TOSHIBA;
+#ifdef _MSC_VER
+	free(cString);
+#endif
+	return ret;
+} //dcmStrManufacturer
+
+#ifdef _MSC_VER //Microsoft nomenclature for packed structures is different...
+    #pragma pack(2)
+    typedef struct {
+        char name[64]; //null-terminated
+        int32_t vm;
+        char vr[4]; //  possibly nul-term string
+        int32_t syngodt;//  ??
+        int32_t nitems;// number of items in CSA
+        int32_t xx;// maybe == 77 or 205
+    } TCSAtag; //Siemens csa tag structure
+    typedef struct {
+        int32_t xx1, xx2_Len, xx3_77, xx4;
+    } TCSAitem; //Siemens csa item structure
+    #pragma pack()
+#else
+    typedef struct __attribute__((packed)) {
+        char name[64]; //null-terminated
+        int32_t vm;
+        char vr[4]; //  possibly nul-term string
+        int32_t syngodt;//  ??
+        int32_t nitems;// number of items in CSA
+        int32_t xx;// maybe == 77 or 205
+    } TCSAtag; //Siemens csa tag structure
+    typedef struct __attribute__((packed)) {
+        int32_t xx1, xx2_Len, xx3_77, xx4;
+    } TCSAitem; //Siemens csa item structure
+#endif
+
+float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) {
+    //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
+    //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion
+    TCSAitem itemCSA;
+    *ItemsOK = 0;
+    if (nItems < 1)  return 0.0f;
+    Floats[1] = 0;
+    int lPos = 0;
+    for (int lI = 1; lI <= nItems; lI++) {
+        memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+        lPos +=sizeof(itemCSA);
+        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));
+            lPos += ((itemCSA.xx2_Len +3)/4)*4;
+            //printf(" %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
+        }
+    } //for each item
+    return Floats[1];
+} //csaMultiFloat()
+
+bool csaIsPhaseMap (unsigned char buff[], int nItems) {
+    //returns true if the tag "ImageHistory" has an item named "CC:ComplexAdd"
+    TCSAitem itemCSA;
+    if (nItems < 1)  return false;
+    int lPos = 0;
+    for (int lI = 1; lI <= nItems; lI++) {
+        memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+        lPos +=sizeof(itemCSA);
+        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));
+            lPos += ((itemCSA.xx2_Len +3)/4)*4;
+            //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
+            if (strcmp(cString, "CC:ComplexAdd") == 0)
+                return true;
+#ifdef _MSC_VER
+            free(cString);
+#endif
+        }
+    } //for each item
+    return false;
+} //csaIsPhaseMap()
+
+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]);
+    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
+    int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24);
+    if (buff[lPos+4] != 77) return EXIT_FAILURE;
+    lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0
+    TCSAtag tagCSA;
+    TCSAitem itemCSA;
+    int itemsOK;
+    float lFloats[7];
+    for (int lT = 1; lT <= lnTag; lT++) {
+        memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag
+        lPos +=sizeof(tagCSA);
+        if (isVerbose > 1) //extreme verbosity: show every CSA tag
+        	printf("%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);
+            else if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0)
+                CSA->mosaicSlices = (int) round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            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]);
+                    CSA->dtiV[0] = 0.0;
+                }
+                CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag
+            }
+            else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){
+                CSA->dtiV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
+                CSA->dtiV[2] = lFloats[2];
+                CSA->dtiV[3] = lFloats[3];
+                if (isVerbose)
+                    printf("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]);
+            } 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
+				float * sliceTimes = (float *)malloc(sizeof(float) * (tagCSA.nitems + 1));
+#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++)
+        			dti4D->S[z].sliceTiming = -1.0;
+
+                if (itemsOK <= kMaxDTI4D)
+                	for (int z = 1; z <= itemsOK; z++)
+                		dti4D->S[z-1].sliceTiming = sliceTimes[z];
+                CSA->multiBandFactor = 1;
+                timeValue1 = sliceTimes[1];
+                int nTimeZero = 0;
+                if (sliceTimes[1] == 0)
+                    	nTimeZero++;
+                int minTimeIndex = 1;
+                int maxTimeIndex = minTimeIndex;
+                minTimeValue = sliceTimes[1];
+                maxTimeValue = minTimeValue;
+                if (isVerbose)
+                    printf("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]);
+                    if (sliceTimes[z] == 0)
+                    	nTimeZero++;
+                    if (sliceTimes[z] < minTimeValue) {
+						minTimeValue = sliceTimes[z];
+						minTimeIndex = (float) z;
+					}
+                    if (sliceTimes[z] > maxTimeValue) {
+                        maxTimeValue = sliceTimes[z];
+                        maxTimeIndex = (float) z;
+                    }
+                    if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++;
+				}
+                if (isVerbose)
+                    printf("\n");
+                CSA->slice_start = minTimeIndex -1;
+                CSA->slice_end = maxTimeIndex -1;
+                if (minTimeIndex == 2)
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2
+                else if (minTimeIndex == (itemsOK-1))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 2,4,1,3 or   5,2,4,1,3
+                else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_INC; // e.g. 1,2,3,4
+                else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC; //e.g. 1,3,2,4
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-2] > sliceTimes[itemsOK-1]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC; //e.g. 4,3,2,1  or 5,4,3,2,1
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-2] < sliceTimes[itemsOK-1]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC; //e.g.  4,2,3,1 or 3,5,2,4,1
+                else {
+                    /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems];
+                     for (int z = 1; z <= itemsOK; z++)
+                     [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");
+                }
+                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);
+                	CSA->sliceOrder = NIFTI_SLICE_UNKNOWN;
+
+                }
+#ifdef _MSC_VER
+				free(sliceTimes);
+#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)
+                CSA->phaseEncodingDirectionPositive = (int) round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            for (int lI = 1; lI <= tagCSA.nitems; lI++) {
+                memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+                lPos +=sizeof(itemCSA);
+                lPos += ((itemCSA.xx2_Len +3)/4)*4;
+            }
+        } //if at least 1 item
+    }// for lT 1..lnTag
+    return EXIT_SUCCESS;
+} // readCSAImageHeader()
+
+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
+	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
+#else
+	char cString[lByteLength + 1];
+#endif
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    cString[lByteLength] = 0; //null terminate
+    char *temp=( char *)malloc(lByteLength+1);
+    int f = 0,lStart = 0;
+    bool isOK = false;
+    for (int i = 0; i <= lByteLength; i++) {
+        if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true;
+        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);
+            if (f < lnFloats) {
+                f ++;
+                lFloats[f] = (float) atof(temp);
+                isOK = false;
+                //printf("%d == %f\n", f, atof(temp));
+            } //if f <= nFloats
+            lStart = i+1;
+        } //if isOK
+    }  //for i to length
+    free(temp);
+#ifdef _MSC_VER
+	free(cString);
+#endif
+} //dcmMultiFloat()
+
+float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
+#ifdef _MSC_VER
+	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
+#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
+	free(cString);
+#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
+    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;
+    for (int i = 0; i < 10; i++) h->data_type[i] = 0;
+    for (int i = 0; i < 16; i++) h->intent_name[i] = 0;
+    if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3)) {
+        h->intent_code = NIFTI_INTENT_ESTIMATE; //make sure we treat this as RGBRGB...RGB
+        h->datatype = DT_RGB24;
+    } else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1))
+        h->datatype = DT_UINT8;
+    else if ((d.bitsAllocated == 12) && (d.samplesPerPixel == 1))
+        h->datatype = DT_INT16;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned))
+        h->datatype = DT_INT16;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned))
+        h->datatype = DT_UINT16;
+    else if ((d.bitsAllocated == 32) && (d.isFloat))
+        h->datatype = DT_FLOAT32;
+    else if (d.bitsAllocated == 32)
+        h->datatype = DT_INT32;
+    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
+        return EXIT_FAILURE;
+    }
+    if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16))
+        h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767
+    for (int i = 0; i < 8; i++) {
+        h->pixdim[i] = 0.0f;
+        h->dim[i] = 0;
+    }
+    //next items listed as unused in NIfTI format, but zeroed for consistency across runs
+	h->extents = 0;
+    h->session_error = 0;
+    h->glmin = 0; //unused, but make consistent
+    h->glmax = 0; //unused, but make consistent
+    h->regular = 114; //in legacy Analyze this was always 114
+    //these are important
+    h->scl_inter = d.intenIntercept;
+    h->scl_slope = d.intenScale;
+    h->cal_max = 0;
+    h->cal_min = 0;
+    h->magic[0]='n';
+    h->magic[1]='+';
+    h->magic[2]='1';
+    h->magic[3]='\0';
+    h->vox_offset = (float) d.imageStart;
+    if (d.bitsAllocated == 12)
+    	h->bitpix = 16 * d.samplesPerPixel;
+    else
+    	h->bitpix = d.bitsAllocated * d.samplesPerPixel;
+    h->pixdim[1] = d.xyzMM[1];
+    h->pixdim[2] = d.xyzMM[2];
+    h->pixdim[3] = d.xyzMM[3];
+    h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec
+    h->dim[1] = d.xyzDim[1];
+    h->dim[2] = d.xyzDim[2];
+    h->dim[3] = d.xyzDim[3];
+    h->dim[4] = d.xyzDim[4];
+    if (h->dim[4] < 2)
+        h->dim[0] = 3;
+    else
+        h->dim[0] = 4;
+    for (int i = 0; i <= 3; i++) {
+        h->srow_x[i] = 0.0f;
+        h->srow_y[i] = 0.0f;
+        h->srow_z[i] = 0.0f;
+    }
+    h->slice_start = 0;
+    h->slice_end = 0;
+    h->srow_x[0] = -1;
+    h->srow_y[2] = 1;
+    h->srow_z[1] = -1;
+	h->srow_x[3] = ((float) h->dim[1] / 2);
+	h->srow_y[3] = -((float)h->dim[3] / 2);
+	h->srow_z[3] = ((float)h->dim[2] / 2);
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->toffset = 0;
+    h->intent_code = NIFTI_INTENT_NONE;
+    h->dim_info = 0; //Freq, Phase and Slice all unknown
+    h->xyzt_units = NIFTI_UNITS_MM + NIFTI_UNITS_SEC;
+    h->slice_duration = 0; //avoid +inf/-inf, NaN
+    h->intent_p1 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p2 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p3 = 0;  //avoid +inf/-inf, NaN
+    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);
+    return EXIT_SUCCESS;
+} // headerDcm2Nii()
+
+bool isFloatDiff (float a, float b) {
+    return (fabs (a - b) > FLT_EPSILON);
+} //isFloatDiff()
+
+mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) {
+    // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!]
+    mat33 ret;
+    for (int r=0; r<3; r++) {
+        for(int c=0; c<3; c++)
+            ret.m[r][c] = m.m[r][v.v[c]-1];
+    }
+    return ret;
+} //nifti_mat33_reorder_cols()
+
+void changeExt (char *file_name, const char* ext) {
+    char *p_extension;
+    p_extension = strrchr(file_name, '.');
+    if (p_extension)
+    {
+        strcpy(++p_extension, 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
+    strcpy(d.seriesDescription, ""); //erase dummy with empty
+    strcpy(d.sequenceName, ""); //erase dummy with empty
+    strcpy(d.scanningSequence, "");
+    FILE *fp = fopen(parname, "r");
+    if (fp == NULL) return d;
+#define LINESZ 2048
+#define	kSlice	0
+#define	kEcho	1
+#define	kDyn	2
+#define	kCardiac	3
+#define	kImageType	4
+#define	kSequence	5
+#define	kIndex	6
+#define	kBitsPerVoxel	7
+#define	kXdim	9
+#define	kYdim	10
+#define	kRI	11
+#define	kRS	12
+#define	kSS	13
+#define	kAngulationAPs	16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z!
+#define	kAngulationFHs	17
+#define	kAngulationRLs	18
+#define	kPositionAP	19
+#define	kPositionFH	20
+#define	kPositionRL	21
+#define	kThickmm	22
+#define	kGapmm	23
+#define kSliceOrients 25
+#define	kXmm	28
+#define	kYmm	29
+#define	kTEcho	30
+#define	kDynTime	31
+#define	kGradientNumber 42
+#define	kbval 33
+#define	kv1	47
+#define	kv2	45
+#define	kv3	46
+#define	kASL	48
+    char buff[LINESZ];
+    float intenScalePhilips = 0.0f;
+    float maxBValue = 0.0f;
+    float maxDynTime = 0.0f;
+    float minDynTime = 999999.0f;
+    bool ADCwarning = false;
+    int parVers = 0;
+    int nCols = 26;
+    int slice = 0;
+    //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order
+    const int kMaxCols = 49;
+    float *cols = (float *)malloc(sizeof(float) * kMaxCols);
+    char *p = fgets (buff, LINESZ, fp);
+    bool isIntenScaleVaries = false;
+    bool isIndexSequential = true;
+    for (int i = 0; i < kMaxDTI4D; i++) {
+        dti4D->S[i].V[0] = -1.0;
+        dti4D->S[i].sliceTiming = -1.0;
+    }
+    //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI));
+    while (p) {
+        if (strlen(buff) < 1)
+            continue;
+        if (buff[0] == '#') { //comment
+            char Comment[7][50];
+            sscanf(buff, "# %s %s\n", Comment[0], Comment[1]);
+            if (strcmp(Comment[1], "TRYOUT") == 0) {
+                sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3]
+                       ,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
+                    return d;
+                    //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns
+                } else if (parVers < 41)
+                    nCols = 32; //e.g PAR 4.0
+                else if (parVers < 42)
+                    nCols = 47; //e.g. PAR 4.1
+                else
+                    nCols = kMaxCols; //e.g. PAR 4.2
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '#' comment
+        if (buff[0] == '.') { //tag
+            char Comment[8][50];
+            sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]);
+
+            if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) {
+                d.acquNum = atoi( Comment[3]);
+                d.seriesNum = d.acquNum;
+            }
+            if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0))
+                d.TR = (float) atof(Comment[4]);
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.patientName, Comment[3]);
+                strcat(d.patientName, Comment[4]);
+                strcat(d.patientName, Comment[5]);
+                strcat(d.patientName, Comment[6]);
+                strcat(d.patientName, Comment[7]);
+                //printf("%s\n",d.patientName);
+
+            }
+            if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.protocolName, Comment[3]);
+                strcat(d.protocolName, Comment[4]);
+                strcat(d.protocolName, Comment[5]);
+                strcat(d.protocolName, Comment[6]);
+                strcat(d.protocolName, Comment[7]);
+                //printf("%s\n",d.protocolName);
+            }
+            if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
+                strcpy(d.studyDate, Comment[3]);
+                strcpy(d.studyTime, Comment[5]);
+                //to do convert to traditional DICOM style date time
+            }
+            if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.stackOffcentre[2] = (float) atof(Comment[5]);
+				d.stackOffcentre[3] = (float) atof(Comment[6]);
+				d.stackOffcentre[1] = (float) atof(Comment[7]);
+            }
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.patientOrient[0] = toupper(Comment[3][0]);
+                d.patientOrient[1] = toupper(Comment[4][0]);
+                d.patientOrient[2] = toupper(Comment[5][0]);
+                d.patientOrient[3] = 0;
+            }
+            if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) {
+                d.xyzDim[3] = atoi(Comment[5]);
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '.' tag
+        if (strlen(buff) < 24) { //empty line
+            p = fgets (buff, LINESZ, fp);//get next line
+            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
+            free (cols);
+            return d;
+        }
+        for (int i = 0; i < nCols; i++)
+            cols[i] = strtof(p, &p); // p+1 skip comma, read a float
+        if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3...
+        slice ++;
+        if (slice == 1) {
+            //for (int i = 0; i < nCols; i++)
+            //    cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices
+            d.xyzDim[1] = (int) cols[kXdim];
+			d.xyzDim[2] = (int) cols[kYdim];
+            d.xyzMM[1] = cols[kXmm];
+            d.xyzMM[2] = cols[kYmm];
+            d.xyzMM[3] = cols[kThickmm] + cols[kGapmm];
+            d.patientPosition[1] = cols[kPositionRL];
+            d.patientPosition[2] = cols[kPositionAP];
+            d.patientPosition[3] = cols[kPositionFH];
+            d.angulation[1] = cols[kAngulationRLs];
+            d.angulation[2] = cols[kAngulationAPs];
+            d.angulation[3] = cols[kAngulationFHs];
+			d.sliceOrient = (int) cols[kSliceOrients];
+            d.TE = cols[kTEcho];
+			d.bitsAllocated = (int) cols[kBitsPerVoxel];
+			d.bitsStored = (int) cols[kBitsPerVoxel];
+            d.intenIntercept = cols[kRI];
+            d.intenScale = cols[kRS];
+            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
+                return d;
+            }
+            if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) ||
+                                                              (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) ||
+                                                              (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position
+                d.patientPositionSequentialRepeats = slice-1;
+
+            if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI]))
+                isIntenScaleVaries = true;
+        }
+        if (cols[kImageType] == 0) d.isHasMagnitude = true;
+        if (cols[kImageType] != 0) d.isHasPhase = true;
+        if (cols[kDynTime] > maxDynTime) maxDynTime = cols[kDynTime];
+        if (cols[kDynTime] < minDynTime) minDynTime = cols[kDynTime];
+        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) ) {
+                if (dti4D->S[dir-1].V[0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2)
+                //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug
+                //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof.
+                ADCwarning = true;
+            }*/
+            if (cols[kSlice] == 1) { //only first slice
+                d.CSA.numDti++;
+                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]);
+                    }
+                    dti4D->S[dir-1].V[0] = cols[kbval];
+                    dti4D->S[dir-1].V[1] = cols[kv1];
+                    dti4D->S[dir-1].V[2] = cols[kv2];
+                    dti4D->S[dir-1].V[3] = cols[kv3];
+					if (cols[kbval] > maxBValue)
+						maxBValue = cols[kbval];
+                } //save DTI direction
+
+            }
+        } //if DTI directions
+        //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
+        p = fgets (buff, LINESZ, fp);//get next line
+    }
+
+    free (cols);
+    fclose (fp);
+    d.manufacturer = kMANUFACTURER_PHILIPS;
+    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);
+        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");
+
+    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;
+    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);
+    mat33 R = nifti_mat33_mul( rx,ry );
+    R = nifti_mat33_mul( R,rz);
+    ivec3 ixyz = setiVec3(1,2,3);
+    if (d.sliceOrient == kSliceOrientSag) {
+        ixyz = setiVec3(2,3,1);
+        for (int r = 0; r < 3; r++)
+            for (int c = 0; c < 3; c++)
+                if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns
+    }else if (d.sliceOrient == kSliceOrientCor) {
+        ixyz = setiVec3(1,3,2);
+        for (int r = 0; r < 3; r++)
+            R.m[r][2] = -R.m[r][2]; //invert rows of final column
+    }
+    R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix
+    d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0];
+    d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1];
+    mat33 diag;
+    LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f,  0.0f,d.xyzMM[2],0.0f,  0.0f,0.0f, d.xyzMM[3]);
+    R= nifti_mat33_mul( R, diag );
+    mat44 R44;
+    LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1],
+               R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2],
+               R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]);
+    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;
+    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]);
+    eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye
+    R44= nifti_mat44_mul( R44 , eye );
+    vec4 y;
+    y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=(float) d.xyzDim[3]-1.0f; y.v[3]=1.0f;
+    y= nifti_vect44mat44_mul(y, R44 );
+    int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k)
+    if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i)
+        if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j)
+            if  (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) {
+                d.patientPosition[1] = R44.m[0][3];
+                d.patientPosition[2] = R44.m[1][3];
+                d.patientPosition[3] = R44.m[2][3];
+                d.patientPositionLast[1] = y.v[0];
+                d.patientPositionLast[2] = y.v[1];
+                d.patientPositionLast[3] = y.v[2];
+            }else {
+                //d.patientPosition
+                d.patientPosition[1] = y.v[0];
+                d.patientPosition[2] = y.v[1];
+                d.patientPosition[3] = y.v[2];
+                d.patientPositionLast[1] = R44.m[0][3];
+                d.patientPositionLast[2] = R44.m[1][3];
+                d.patientPositionLast[3] = R44.m[2][3];
+            }
+    //finish up
+    changeExt (parname, "REC");
+    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");
+        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);
+    	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()
+
+size_t nii_SliceBytes(struct nifti_1_header hdr) {
+    //size of 2D slice
+    size_t imgsz = hdr.bitpix/8;
+    for (int i = 1; i < 3; i++)
+        if (hdr.dim[i]  > 1)
+            imgsz = imgsz * hdr.dim[i];
+    return imgsz;
+} //nii_ImgBytes()
+
+size_t nii_ImgBytes(struct nifti_1_header hdr) {
+    size_t imgsz = hdr.bitpix/8;
+    for (int i = 1; i < 8; i++)
+        if (hdr.dim[i]  > 1)
+            imgsz = imgsz * hdr.dim[i];
+    return imgsz;
+} //nii_ImgBytes()
+
+unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) {
+    //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html
+    if (nMosaicSlices < 2) return inImg;
+    //Byte inImg[ [img length] ];
+    //[img getBytes:&inImg length:[img length]];
+    int nRowCol = (int) ceil(sqrt((double) nMosaicSlices));
+    int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8;
+    int lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8;
+    int col = 0;
+    int row = 0;
+    int lOutPos = 0;
+    hdr->dim[1] = hdr->dim[1]/nRowCol;
+    hdr->dim[2] = hdr->dim[2]/nRowCol;
+    hdr->dim[3] = nMosaicSlices;
+    size_t imgsz = nii_ImgBytes(*hdr);
+    unsigned char *outImg = (unsigned char *)malloc(imgsz);
+    for (int m=1; m <= nMosaicSlices; m++) {
+        int lPos = (row * rowBytes) + (col * colBytes);
+        for (int y = 0; y < hdr->dim[2]; y++) {
+            memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes
+            lPos += lineBytes;
+            lOutPos +=colBytes;
+        }
+        col ++;
+        if (col >= nRowCol) {
+            row ++;
+            col = 0;
+        } //start new column
+    } //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
+    }*/
+    /*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;
+     memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes
+     int lOutPos = sliceBytes * (hdr->dim[3]-1);
+     int lPos = 0;
+     for (int m=0; m < nMosaicSlices; m++) {
+     memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes);
+     lPos += sliceBytes;
+     lOutPos -= sliceBytes;
+     }
+     }*/
+    free(inImg);
+    return outImg;
+} // nii_demosaic()
+
+unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    size_t lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24) && (hdr->intent_code == NIFTI_INTENT_NONE)) {
+        //we use the intent code to indicate planar vs triplet...
+        lineBytes = hdr->dim[1];
+        dim3to7 = dim3to7 * 3;
+    } //rgb data saved planar (RRR..RGGGG..GBBB..B
+#ifdef _MSC_VER
+	unsigned char * line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes));
+#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
+        size_t slBottom = (size_t)sl*sliceBytes;
+        size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes;
+        for (int y = 0; y < halfY; y++) {
+            //swap order of lines
+            memcpy(line, &bImg[slBottom], lineBytes);//memcpy(&line, &bImg[slBottom], lineBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], lineBytes);
+            memcpy(&bImg[slTop], line, lineBytes);//tpx memcpy(&bImg[slTop], &line, lineBytes);
+            slTop -= lineBytes;
+            slBottom += lineBytes;
+        } //for y
+    } //for each slice
+#ifdef _MSC_VER
+	free(line);
+#endif
+    return bImg;
+} // nii_flipImgY()
+
+unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
+    if (halfZ < 1) return bImg;
+    int dim4to7 = 1;
+    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
+	unsigned char * slice = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes));
+#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;
+        for (int z = 0; z < halfZ; z++) {
+            //swap order of lines
+            memcpy(slice, &bImg[slBottom], sliceBytes); //TPX memcpy(&slice, &bImg[slBottom], sliceBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes);
+            memcpy(&bImg[slTop], slice, sliceBytes); //TPX
+            slTop -= sliceBytes;
+            slBottom += sliceBytes;
+        } //for Z
+    } //for each volume
+#ifdef _MSC_VER
+	free(slice);
+#endif
+    return bImg;
+} // nii_flipImgZ()
+
+unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
+    //flip slice order
+    if (h->dim[3] < 2) return bImg;
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0.0f,0.0f,(float) h->dim[3]-1.0f);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipZ;
+    LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f);
+    s= nifti_mat33_mul( s , mFlipZ );
+    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);
+    return nii_flipImgZ(bImg,h);
+}// nii_flipZ()
+
+unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0,(float) h->dim[2]-1,0);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipY;
+    LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f);
+
+    s= nifti_mat33_mul( s , mFlipY );
+    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);
+    return nii_flipImgY(bImg,h);
+}// nii_flipY()
+
+/*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");
+	int nVox = nii_ImgBytes(hdr) / (hdr.bitpix/8);
+    for (int i=(nVox-1); i >= 0; i--) {
+    	int i16 = i * 2;
+    	int i12 = floor(i * 1.5);
+    	uint16_t val;
+    	if ((i % 2) != 1) {
+    		val = (img[i12+0] << 4) + (img[i12+1] >> 4);
+    	} else {
+    		val = ((img[i12+0] & 0x0F) << 8) + img[i12+1];
+		}
+
+		//if ((i % 2) != 1) {
+    	//	val = img[i12+0]  + ((img[i12+1] & 0xF0) << 4);
+    	//} else {
+    	//	val = (img[i12+0] & 0x0F) + (img[i12+1] << 4);
+		//}
+		val = val & 0xFFF;
+        img[i16+0] = val & 0xFF;
+        img[i16+1] = (val >> 8) & 0xFF;
+    }
+} //conv12bit16bit()*/
+
+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");
+	int nVox = (int) nii_ImgBytes(hdr) / (hdr.bitpix/8);
+    for (int i=(nVox-1); i >= 0; i--) {
+    	int i16 = i * 2;
+    	int i12 = floor(i * 1.5);
+    	uint16_t val;
+    	if ((i % 2) != 1) {
+    		val = img[i12+1] + (img[i12+0] << 8);
+    		val = val >> 4;
+    	} else {
+    		val = img[i12+0] + (img[i12+1] << 8);
+		}
+        img[i16+0] = val & 0xFF;
+        img[i16+1] = (val >> 8) & 0xFF;
+    }
+} //conv12bit16bit()
+
+unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr, int bitsAllocated) {
+    size_t imgsz = nii_ImgBytes(hdr);
+    size_t imgszRead = imgsz;
+    if (bitsAllocated == 12)
+         imgszRead = round(imgsz * 0.75);
+    FILE *file = fopen(imgname , "rb");
+	if (!file) {
+         printf("Error: unable to open %s\n", imgname);
+         return NULL;
+    }
+	fseek(file, 0, SEEK_END);
+	long long fileLen=ftell(file);
+    if (fileLen < (imgszRead+hdr.vox_offset)) {
+        printf("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);
+	fclose(file);
+	if (bitsAllocated == 12)
+	 conv12bit16bit(bImg, hdr);
+    return bImg;
+} //nii_loadImg()
+
+unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) {
+                        //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+    if (bImg == NULL) return NULL;
+                        if (hdr->datatype != DT_RGB24) return bImg;
+                        if (isPlanar == 0) return bImg;//return nii_bgr2rgb(bImg,hdr);
+                        int dim3to7 = 1;
+                        for (int i = 3; i < 8; i++)
+                            if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+                        //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
+                        unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
+#else
+                        unsigned char  slice24[ sliceBytes24 ];
+#endif
+    int sliceOffsetRGB = 0;
+                        int sliceOffsetR = 0;
+                        int sliceOffsetG = sliceOffsetR + sliceBytes8;
+                        int sliceOffsetB = sliceOffsetR + 2*sliceBytes8;
+                        for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+                            memcpy(slice24, &bImg[sliceOffsetRGB], sliceBytes24);
+                            //int sliceOffsetG = sliceOffsetR + sliceBytes8;
+                            //int sliceOffsetB = sliceOffsetR + 2*sliceBytes8;
+                            int i = 0;
+                            for (int rgb = 0; rgb < sliceBytes8; rgb++) {
+                                bImg[i++] =slice24[sliceOffsetR+rgb];
+                                bImg[i++] =slice24[sliceOffsetG+rgb];
+                                bImg[i++] =slice24[sliceOffsetB+rgb];
+                            }
+                            sliceOffsetRGB += sliceBytes24;
+                        } //for each slice
+#ifdef _MSC_VER
+                        free(slice24);
+#endif
+                        return bImg;
+} //nii_rgb2Planar()
+
+unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) {
+    //DICOM data saved in triples RGBRGBRGB, Analyze RGB saved in planes RRR..RGGG..GBBBB..B
+    if (bImg == NULL) return NULL;
+    if (hdr->datatype != DT_RGB24) return bImg;
+    if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr);
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    //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
+	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);
+    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);
+        int sliceOffsetG = sliceOffsetR + sliceBytes8;
+        int sliceOffsetB = sliceOffsetR + 2*sliceBytes8;
+        int i = 0;
+        int j = 0;
+        for (int rgb = 0; rgb < sliceBytes8; rgb++) {
+            bImg[sliceOffsetR+j] =slice24[i++];
+            bImg[sliceOffsetG+j] =slice24[i++];
+            bImg[sliceOffsetB+j] =slice24[i++];
+            j++;
+        }
+        sliceOffsetR += sliceBytes24;
+    } //for each slice
+#ifdef _MSC_VER
+	free(slice24);
+#endif
+    return bImg;
+} //nii_rgb2Planar()
+
+unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){
+    //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file
+    //WARNING: do this BEFORE nii_check16bitUnsigned!!!!
+    //if (hdr->datatype != DT_INT16) return img;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return img;
+    float * img32=(float*)malloc(nVox*sizeof(float));
+    if (hdr->datatype == DT_UINT8) {
+        uint8_t * img8i = (uint8_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img8i[i];
+    } else if (hdr->datatype == DT_UINT16) {
+        uint16_t * img16ui = (uint16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16ui[i];
+    } else if (hdr->datatype == DT_INT16) {
+        int16_t * img16i = (int16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16i[i];
+    } else if (hdr->datatype == DT_INT32) {
+        int32_t * img32i = (int32_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = (float) img32i[i];
+    }
+    free (img); //release previous image
+    for (int i=0; i < nVox; i++)
+        img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter;
+    hdr->scl_slope = 1;
+    hdr->scl_inter = 0;
+    hdr->datatype = DT_FLOAT;
+    hdr->bitpix = 32;
+    return (unsigned char*) img32;
+} //nii_iVaries()
+
+unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) {
+    //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension
+    int dim4to7 = 1;
+    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);
+    if ((dim4to7 % seqRepeats) != 0) {
+        printf("Error: 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;
+    uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8;
+    uint64_t seqBytes = sliceBytes * seqRepeats;
+    uint64_t typeBytes = seqBytes * hdr->dim[3];
+    uint64_t imgSz = nii_ImgBytes(*hdr);
+    //this uses a lot of RAM, someday this could be done in place...
+    unsigned char *outImg = (unsigned char *)malloc( imgSz);
+    //memcpy(&tempImg[0], &bImg[0], imgSz);
+    uint64_t origPos = 0;
+    uint64_t Pos = 0; //
+
+    for (int t = 0; t < typeRepeats; t++) { //for each volume
+        for (int s = 0; s < seqRepeats; s++) {
+            origPos = (t*typeBytes) +s*sliceBytes;
+            for (int z = 0; z < hdr->dim[3]; z++) { //for each slice
+                memcpy( &outImg[Pos],&bImg[origPos], sliceBytes);
+                Pos += sliceBytes;
+                origPos += seqBytes;
+            }
+        }//for s
+    }
+    free(bImg);
+    return outImg;
+} //nii_XYTZ_XYZT()
+
+unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){
+    if (hdr->bitpix < 9) return img;
+    uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8);
+    void *ar = (void*) img;
+    if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar );
+    if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar );
+    if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar );
+    return img;
+} //nii_byteswap()
+
+#ifdef myEnableJasper
+unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, int compressFlag) {
+    if (jas_init()) {
+        return NULL;
+    }
+    jas_stream_t *in;
+    jas_image_t *image;
+    jas_setdbglevel(0);
+    if (!(in = jas_stream_fopen(imgname, "rb"))) {
+        printf( "Error: 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);
+        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);
+        return NULL;
+    }
+    int numcmpts;
+    int cmpts[4];
+    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");
+            numcmpts = 3;
+            if ((cmpts[0] = jas_image_getcmptbytype(image,
+                                                    JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
+                (cmpts[1] = jas_image_getcmptbytype(image,
+                                                    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");
+                return NULL;
+            }
+            break;
+        case JAS_CLRSPC_FAM_GRAY:
+            if (jas_image_clrspc(image) != JAS_CLRSPC_SGRAY)
+                printf("Warning: 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");
+                return NULL;
+            }
+            break;
+        default:
+            printf("error: unsupported color space\n");
+            return NULL;
+            break;
+    }
+    int width = jas_image_cmptwidth(image, cmpts[0]);
+    int height = jas_image_cmptheight(image, cmpts[0]);
+    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());
+    #endif
+    for (int cmptno = 0; cmptno < numcmpts; ++cmptno) {
+        if (jas_image_cmptwidth(image, cmpts[cmptno]) != width ||
+            jas_image_cmptheight(image, cmpts[cmptno]) != height ||
+            jas_image_cmptprec(image, cmpts[cmptno]) != prec ||
+            jas_image_cmptsgnd(image, cmpts[cmptno]) != sgnd ||
+            jas_image_cmpthstep(image, cmpts[cmptno]) != jas_image_cmpthstep(image, 0) ||
+            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");
+            return NULL;
+        }
+    }
+    //extract the data
+    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");
+        return NULL;
+    }
+    jas_seqent_t v;
+    unsigned char *img = (unsigned char *)malloc(imgbytes);
+    uint16_t * img16ui = (uint16_t*) img; //unsigned 16-bit
+    int16_t * img16i = (int16_t*) img; //signed 16-bit
+    if (sgnd) bpp = -bpp;
+    if (bpp == -1) {
+        printf("Error: Signed 8-bit DICOM?\n");
+        return NULL;
+    }
+    jas_matrix_t *data;
+    jas_seqent_t *d;
+    data = 0;
+    int cmptno, y, x;
+    int pix = 0;
+    for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
+        if (!(data = jas_matrix_create(1, width))) {
+            free(img);
+            return NULL;
+        }
+    }
+    //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
+    for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
+        for (y = 0; y < height; ++y) {
+            if (jas_image_readcmpt(image, cmpts[cmptno], 0, y, width, 1, data)) {
+                free(img);
+                return NULL;
+            }
+            d = jas_matrix_getref(data, 0, 0);
+            for (x = 0; x < width; ++x) {
+                v = *d;
+                switch (bpp) {
+                    case 1:
+                        img[pix] = v;
+                        break;
+                    case 2:
+                        img16ui[pix] = v;
+                        break;
+                    case -2:
+                        img16i[pix] = v;
+                        break;
+                }
+                pix ++;
+                ++d;
+            }//for x
+        } //for y
+    } //for each component
+    jas_matrix_destroy(data);
+    jas_image_destroy(image);
+    jas_image_clearfmts();
+    return img;
+} //nii_loadImgCoreJasper()
+#endif
+
+struct TJPEG {
+    long offset;
+    long size;
+};
+
+TJPEG *  decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbose, int frames, bool isLittleEndian) {
+#define abortGoto() free(lOffsetRA); return NULL;
+    TJPEG *lOffsetRA = (TJPEG*) malloc(frames * sizeof(TJPEG));
+    FILE *reader = fopen(fn, "rb");
+    fseek(reader, 0, SEEK_END);
+    long lRawSz = ftell(reader)- skipBytes;
+    if (lRawSz <= 8) {
+        printf("Error opening %s\n", fn);
+        abortGoto(); //read failure
+    }
+    fseek(reader, skipBytes, SEEK_SET);
+    unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
+    fread(lRawRA, 1, lRawSz, reader);
+    fclose(reader);
+    long lRawPos = 0; //starting position
+    int frame = 0;
+    while ((frame < frames) && ((lRawPos+10) < lRawSz)) {
+        int tag = dcmInt(4,&lRawRA[lRawPos],isLittleEndian);
+        lRawPos += 4; //read tag
+        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);
+        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);
+        } else {
+            lOffsetRA[frame].offset = lRawPos+skipBytes;
+            lOffsetRA[frame].size = tagLength;
+            frame ++;
+        }
+        lRawPos = tagEnd;
+    }
+    free(lRawRA);
+    if (frame < frames) {
+        printf("Only found %d of %d JPEG fragments. Please use dcmdjpeg to uncompress data.\n", frame, frames);
+        abortGoto();
+    }
+    return lOffsetRA;
+}
+
+unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm, bool isVerbose) {
+    //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();
+    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 (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]);
+        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;
+        size_t slicesz = nii_SliceBytes(hdr);
+        size_t imgsz = slicesz * hdr.dim[3];
+        size_t pos = 0;
+        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);
+            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");
+                free(bImg);
+                return NULL;
+            }
+            memcpy(&bImg[pos], ret, slicesz); //dest, src, size
+            free(ret);
+            pos += slicesz;
+        }
+        free(offsetRA);
+        return bImg;
+    }
+    return ret;
+}
+
+#ifndef F_OK
+#define F_OK 0 /* existence check */
+#endif
+
+unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
+    //printf("50 offset %d\n", dcm.imageStart);
+    if( access(imgname, F_OK ) == -1 ) {
+        printf("Error: unable to find '%s'\n", imgname);
+        return NULL;
+    }
+    //load compressed data
+    FILE *f = fopen(imgname, "rb");
+    fseek(f, 0, SEEK_END);
+    int size = (int) ftell(f);
+    size = size - dcm.imageStart;
+    if (size < 8) {
+        printf("Error file too small\n");
+        fclose(f);
+        return NULL;
+    }
+    char *buf = (char *)malloc(size);
+    fseek(f, dcm.imageStart, SEEK_SET);
+    size = (int) fread(buf, 1, size, f);
+    fclose(f);
+    //decode
+    njInit();
+    if (njDecode(buf, size)) {
+        printf("Error decoding JPEG image.\n");
+        return NULL;
+    }
+    free(buf);
+    unsigned char *bImg = (unsigned char *)malloc(njGetImageSize());
+    memcpy(bImg, njGetImage(), njGetImageSize()); //dest, src, size
+    njDone();
+    return bImg;
+}
+
+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;
+    unsigned char * img;
+    if (dcm.compressionScheme == kCompress50)  {
+        img = nii_loadImgJPEG50(imgname, *hdr, dcm);
+    } else if (dcm.compressionScheme == kCompressC3) {
+            img = nii_loadImgJPEGC3(imgname, *hdr, dcm, (isVerbose > 0));
+    } else
+    #ifndef myDisableOpenJPEG
+    if ( ((dcm.compressionScheme == kCompress50) || (dcm.compressionScheme == kCompressYes)) && (compressFlag != kCompressNone) )
+        img = nii_loadImgCoreOpenJPEG(imgname, *hdr, dcm, compressFlag);
+    else
+    #else
+       #ifdef myEnableJasper
+        if ((dcm.compressionScheme == kCompressYes) && (compressFlag != kCompressNone) )
+            img = nii_loadImgCoreJasper(imgname, *hdr, dcm, compressFlag);
+        else
+        #endif
+    #endif
+    if (dcm.compressionScheme == kCompressYes) {
+        printf("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))
+        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
+    dcm.isPlanarRGB = true;
+    if (dcm.CSA.mosaicSlices > 1) {
+        img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, dcm.CSA.protocolSliceNumber1);
+        /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip
+         img = nii_flipImgY(img, hdr);
+         #endif*/
+    }
+    if ((!dcm.isFloat) && (iVaries)) img = nii_iVaries(img, hdr);
+    int nAcq = dcm.locationsInAcquisition;
+    if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) {
+        hdr->dim[4] = hdr->dim[3]/nAcq;
+        hdr->dim[3] = nAcq;
+        hdr->dim[0] = 4;
+    }
+    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);
+    return img;
+} //nii_loadImgXL()
+
+int isDICOMfile(const char * fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10 compliant)
+    FILE *fp = fopen(fname, "rb");
+	if (!fp)  return 0;
+	fseek(fp, 0, SEEK_END);
+	long 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);
+	fclose(fp);
+    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))
+    	return 2; //not valid Part 10 file, perhaps DICOM object
+    return 0;
+} //isDICOMfile()
+
+struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D) {
+//struct TDICOMdata readDICOMv(char * fname, bool isVerbose, int compressFlag) {
+	struct TDICOMdata d = clear_dicom_data();
+    strcpy(d.protocolName, ""); //erase dummy with empty
+    strcpy(d.protocolName, ""); //erase dummy with empty
+    strcpy(d.seriesDescription, ""); //erase dummy with empty
+    strcpy(d.sequenceName, ""); //erase dummy with empty
+    //do not read folders - code specific to GCC (LLVM/Clang seems to recognize a small file size)
+
+    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);
+            return d;
+        }
+    }
+    bool isPart10prefix = true;
+    int isOK = isDICOMfile(fname);
+    if (isOK == 0) return d;
+    if (isOK == 2) {
+    	d.isExplicitVR = false;
+    	isPart10prefix = false;
+    }
+
+    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
+		return d;
+	}
+	fseek(file, 0, SEEK_END);
+	long 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
+		return d;
+	}
+	fseek(file, 0, SEEK_SET);
+	//Allocate memory
+	unsigned char *buffer=(unsigned char *)malloc(fileLen+1);
+	if (!buffer) {
+		printf( "Memory error!");
+        fclose(file);
+		return d;
+	}
+	//Read file contents into buffer
+	fread(buffer, fileLen, 1, file);
+	fclose(file);
+	//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)){
+    //		free (buffer);
+    //    	return d;
+    //	}
+    //	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);
+    //}
+    //DEFINE DICOM TAGS
+#define  kUnused 0x0001+(0x0001 << 16 )
+#define  kStart 0x0002+(0x0000 << 16 )
+#define  kTransferSyntax 0x0002+(0x0010 << 16)
+//#define  kSpecificCharacterSet 0x0008+(0x0005 << 16 ) //someday we should handle foreign characters...
+#define  kImageTypeTag 0x0008+(0x0008 << 16 )
+#define  kStudyDate 0x0008+(0x0020 << 16 )
+#define  kAcquisitionDate 0x0008+(0x0022 << 16 )
+#define  kStudyTime 0x0008+(0x0030 << 16 )
+#define  kAcquisitionTime 0x0008+(0x0032 << 16 )
+#define  kManufacturer 0x0008+(0x0070 << 16 )
+#define  kSeriesDescription 0x0008+(0x103E << 16 ) // '0008' '103E' 'LO' 'SeriesDescription'
+#define  kManufacturersModelName 0x0008+(0x1090 << 16 )
+#define  kDerivationDescription 0x0008+(0x2111 << 16 )
+#define  kComplexImageComponent (uint32_t) 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent'
+#define  kPatientName 0x0010+(0x0010 << 16 )
+#define  kPatientID 0x0010+(0x0020 << 16 )
+#define  kScanningSequence 0x0018+(0x0020 << 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  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  kGantryTilt  0x0018+(0x1120  << 16 )
+#define  kXRayExposure  0x0018+(0x1152  << 16 )
+#define  kFlipAngle  0x0018+(0x1314  << 16 )
+#define  kInPlanePhaseEncodingDirection  0x0018+(0x1312<< 16 ) //CS
+#define  kPatientOrient  0x0018+(0x5100<< 16 )    //0018,5100. patient orientation - 'HFS'
+//#define  kDiffusionBFactorSiemens  0x0019+(0x100C<< 16 ) //   0019;000C;SIEMENS MR HEADER  ;B_value
+#define  kLastScanLoc  0x0019+(0x101B<< 16 )
+#define  kDiffusionDirectionGEX  0x0019+(0x10BB<< 16 ) //DS
+#define  kDiffusionDirectionGEY  0x0019+(0x10BC<< 16 ) //DS
+#define  kDiffusionDirectionGEZ  0x0019+(0x10BD<< 16 ) //DS
+#define  kPatientPosition 0x0020+(0x0032 << 16 )
+#define  kSeriesNum 0x0020+(0x0011 << 16 )
+#define  kAcquNum 0x0020+(0x0012 << 16 )
+#define  kImageNum 0x0020+(0x0013 << 16 )
+#define  kOrientationACR 0x0020+(0x0035 << 16 )
+#define  kOrientation 0x0020+(0x0037 << 16 )
+#define  kImagesInAcquisition 0x0020+(0x1002 << 16 ) //IS
+#define  kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments'
+#define  kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE'
+#define  kSamplesPerPixel 0x0028+(0x0002 << 16 )
+#define  kPlanarRGB 0x0028+(0x0006 << 16 )
+#define  kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4
+#define  kDim2 0x0028+(0x0010 << 16 )
+#define  kDim1 0x0028+(0x0011 << 16 )
+#define  kXYSpacing  0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing'
+#define  kBitsAllocated 0x0028+(0x0100 << 16 )
+#define  kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored'
+#define  kIsSigned 0x0028+(0x0103 << 16 )
+#define  kIntercept 0x0028+(0x1052 << 16 )
+#define  kSlope 0x0028+(0x1053 << 16 )
+#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  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  kIconImageSequence 0x0088+(0x0200 << 16 )
+#define  kDiffusionBFactor  0x2001+(0x1003 << 16 )// FL
+#define  kSliceOrient  0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
+#define  kLocationsInAcquisitionPhilips  0x2001+(0x1018 << 16 )
+#define  kNumberOfDynamicScans  0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans'
+#define  kMRAcquisitionTypePhilips 0x2005+(0x106F << 16)
+#define  kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP'
+#define  kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH'
+#define  kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL'
+#define  kMRStackOffcentreAP 0x2005+(0x1078 << 16)
+#define  kMRStackOffcentreFH 0x2005+(0x1079 << 16)
+#define  kMRStackOffcentreRL 0x2005+(0x107A << 16)
+#define  kPhilipsSlope 0x2005+(0x100E << 16 )
+#define  kDiffusionDirectionRL 0x2005+(0x10B0 << 16)
+#define  kDiffusionDirectionAP 0x2005+(0x10B1 << 16)
+#define  kDiffusionDirectionFH 0x2005+(0x10B2 << 16)
+#define  k2005140F 0x2005+(0x140F << 16)
+#define  kWaveformSq 0x5400+(0x0100 << 16)
+#define  kImageStart 0x7FE0+(0x0010 << 16 )
+#define  kImageStartFloat 0x7FE0+(0x0008 << 16 )
+#define  kImageStartDouble 0x7FE0+(0x0009 << 16 )
+#define kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ
+#define  kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/
+#define  kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined]
+    dti4D->S[0].sliceTiming = -1.0;
+    int nest = 0;
+    double zSpacing = -1.0l; //includes slice thickness plus gap
+    int locationsInAcquisitionGE = 0;
+    int locationsInAcquisitionPhilips = 0;
+    int imagesInAcquisition = 0;
+    uint32_t lLength;
+    uint32_t groupElement;
+    long lPos = 0;
+    if (isPart10prefix) { //for part 10 files, skip preamble and prefix
+    	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
+    }
+    char vr[2];
+    float intenScalePhilips = 0.0;
+    bool isEncapsulatedData = false;
+    bool isOrient = false;
+    bool isIconImageSequence = false;
+    bool isSwitchToImplicitVR = false;
+    bool isSwitchToBigEndian = false;
+    //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2
+    bool is2005140FSQ = false; //for buggy Philips
+    bool is2005140FSQwarned = false; //for buggy Philips
+    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 sqDepth = 0;
+    //long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP)
+    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);
+        else
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToBigEndian = false;
+            d.isLittleEndian = false;
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        }//transfer syntax requests switching endian after group 0002
+        if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToImplicitVR = false;
+            d.isExplicitVR = false;
+        } //transfer syntax requests switching VR after group 0001
+        //uint32_t group = (groupElement & 0xFFFF);
+        lPos += 4;
+    if (((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) && (!isEncapsulatedData)) {
+        //if (((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) ) {
+            vr[0] = 'N';
+            vr[1] = 'A';
+            if (groupElement == kUnnest2) sqDepth--;
+            //if (groupElement == kUnnest2) printf("SQend %d\n", sqDepth);
+
+            //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail
+            lLength = 4;
+        } else if (d.isExplicitVR) {
+            vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1];
+            if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos += 4;
+            } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N'))
+                       || ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'T'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W'))
+                       ) { //VR= UN, OB, OW, SQ  || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q'))
+                lPos = lPos + 4;  //skip 2 byte VR string and 2 reserved bytes = 4 bytes
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos = lPos + 4;  //skip 4 byte length
+            } else if   ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) {
+                lLength = 8; //Sequence Tag
+                is2005140FSQ = (groupElement == k2005140F);
+            } else { //explicit VR with 16-bit length
+                if ((d.isLittleEndian)  )
+                    lLength = buffer[lPos+2] | (buffer[lPos+3] << 8);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8);
+                lPos += 4;  //skip 2 byte VR string and 2 length bytes = 4 bytes
+            }
+        } else { //implicit VR
+            vr[0] = 'N';
+            vr[1] = 'A';
+            if (d.isLittleEndian)
+                lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+            else
+                lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+            lPos += 4;  //we have loaded the 32-bit length
+        } //if explicit else implicit VR
+        if (lLength == 0xFFFFFFFF) {
+            lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length
+            vr[0] = 'S';
+            vr[1] = 'Q';
+        }
+
+        if   ((vr[0] == 'S') && (vr[1] == 'Q')) {
+            sqDepth++;
+            //printf("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) {
+                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);
+        switch ( groupElement ) {
+            case 	kTransferSyntax: {
+                char transferSyntax[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], transferSyntax);
+                //printf("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);
+                    //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);
+                        //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) {
+                //    d.compressionScheme = kCompress50;
+                } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.57") == 0) {
+                    //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");
+                } 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");
+                    //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");
+                } 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");
+                } 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)
+                //    ;
+                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
+                    d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
+                }
+                break;} //{} provide scope for variable 'transferSyntax
+            case kImageTypeTag:
+            	dcmStr (lLength, &buffer[lPos], d.imageType);
+                int slen;
+                slen = (int) strlen(d.imageType);
+				//if (strcmp(transferSyntax, "ORIGINAL_PRIMARY_M_ND_MOSAIC") == 0)
+                if((slen > 5) && !strcmp(d.imageType + slen - 6, "MOSAIC") )
+                	isMosaic = true;
+                //isNonImage 0008,0008 = DERIVED,CSAPARALLEL,POSDISP
+                // attempt to detect non-images, see https://github.com/scitran/data/blob/a516fdc39d75a6e4ac75d0e179e18f3a5fc3c0af/scitran/data/medimg/dcm/mr/siemens.py
+                if((slen > 6) && (strstr(d.imageType, "DERIVED") != NULL) )
+                	d.isNonImage = true;
+                //if((slen > 4) && (strstr(typestr, "DIS2D") != NULL) )
+                //	d.isNonImage = true;
+            	break;
+            case kAcquisitionDate:
+            	char acquisitionDateTxt[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], acquisitionDateTxt);
+                d.acquisitionDate = atof(acquisitionDateTxt);
+            	break;
+            case kStudyDate:
+                dcmStr (lLength, &buffer[lPos], d.studyDate);
+                break;
+            case 	kManufacturer:
+                d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]);
+                break;
+            case 	kComplexImageComponent:
+                d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H');
+                d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A');
+                break;
+            case 	kAcquisitionTime :
+                char acquisitionTimeTxt[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt);
+                d.acquisitionTime = atof(acquisitionTimeTxt);
+                break;
+            case 	kStudyTime :
+                dcmStr (lLength, &buffer[lPos], d.studyTime);
+                break;
+            case 	kPatientName :
+                dcmStr (lLength, &buffer[lPos], d.patientName);
+                break;
+            case kPatientID :
+                dcmStr (lLength, &buffer[lPos], d.patientID);
+                break;
+            case kSeriesDescription: {
+                //if (strlen(d.protocolName) < 1)
+                dcmStr (lLength, &buffer[lPos], d.seriesDescription);
+                break; }
+            case kManufacturersModelName :
+            	dcmStr (lLength, &buffer[lPos], d.manufacturersModelName);
+            	break;
+            case kDerivationDescription : {
+                //strcmp(transferSyntax, "1.2.840.10008.1.2")
+                char derivationDescription[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], derivationDescription);//strcasecmp, strcmp
+                if (strcasecmp(derivationDescription, "MEDCOM_RESAMPLED") == 0) d.isResampled = true;
+                break;
+            }
+            case kProtocolName : {
+                //if ((strlen(d.protocolName) < 1) || (d.manufacturer != kMANUFACTURER_GE)) //GE uses a generic session name here: do not overwrite kProtocolNameGE
+                dcmStr (lLength, &buffer[lPos], d.protocolName); //see also kSequenceName
+                break; }
+            case 	kPatientOrient :
+                dcmStr (lLength, &buffer[lPos], d.patientOrient);
+                break;
+            case kLastScanLoc :
+                d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+                /*case kDiffusionBFactorSiemens :
+                 if (d.manufacturer == kMANUFACTURER_SIEMENS)
+                 printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos]));
+
+                 break;*/
+            case kDiffusionDirectionGEX :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[1] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEY :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[2] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEZ :
+                if (d.manufacturer == kMANUFACTURER_GE) {
+                    d.CSA.dtiV[3] =  dcmStrFloat(lLength, &buffer[lPos]);
+                    d.CSA.numDti = 1;
+                }
+                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
+                    is2005140FSQwarned = true;
+                } else {
+                    patientPositionCount++;
+                    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
+                        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;
+                        } //if different position from 1st slice in file
+                    } //if not first slice in file
+                } //not after 2005,140F
+                break;
+            case 	kInPlanePhaseEncodingDirection:
+                d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol
+                break;
+            case 	kSeriesNum:
+                d.seriesNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kAcquNum:
+                d.acquNum = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kImageNum:
+                //int dx = 3;
+                if (d.imageNum <= 1) d.imageNum = dcmStrInt(lLength, &buffer[lPos]);  //Philips renames each image as image1 in 2001,9000
+                break;
+            case 	kPlanarRGB:
+                d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim3:
+                d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kSamplesPerPixel:
+                d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim2:
+                d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim1:
+                d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kXYSpacing:
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM);
+
+                break;
+            case 	kImageComments:
+                dcmStr (lLength, &buffer[lPos], d.imageComments);
+                break;
+            case 	kLocationsInAcquisitionGE:
+                locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsAllocated :
+                d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsStored :
+                d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html
+                d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kTR :
+                d.TR = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kTE :
+            	d.TE = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kEchoNum :
+                d.echoNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case kMagneticFieldStrength :
+                d.fieldStrength =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kZSpacing :
+                zSpacing = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kPhaseEncodingSteps :
+                phaseEncodingSteps =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case kFlipAngle :
+            	d.flipAngle = 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]);
+            	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);
+                break;
+
+            case 	kIntercept :
+                d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kZThick :
+                d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kCoilSiemens : {
+                if (d.manufacturer == kMANUFACTURER_SIEMENS) {
+                    //see if image from single coil "H12" or an array "HEA;HEP"
+                    char coilStr[kDICOMStr];
+                    dcmStr (lLength, &buffer[lPos], coilStr);
+                    //long coilNum = 0;
+                    char *ptr;
+                    dcmStrDigitsOnly(coilStr);
+                    d.coilNum = (int)strtol(coilStr, &ptr, 10);
+                    if (*ptr != '\0')
+                        d.coilNum = 0;
+                }
+                break; }
+            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 	kNumberOfDynamicScans:
+                d.numberOfDynamicScans =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case	kMRAcquisitionType: //detect 3D acquisition: we can reorient these without worrying about slice time correct or BVEC/BVAL orientation
+            	#ifndef myDisableReorient3dToOrtho
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                #endif
+                break;
+            case kScanningSequence : {
+                dcmStr (lLength, &buffer[lPos], d.scanningSequence);
+                break;
+            }
+            case kSequenceName : {
+                //if (strlen(d.protocolName) < 1) //precedence given to kProtocolName and kProtocolNameGE
+                dcmStr (lLength, &buffer[lPos], d.sequenceName);
+                break;
+            }
+            case	kMRAcquisitionTypePhilips: //kMRAcquisitionType
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                break;
+            case	kAngulationRL:
+                d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationAP:
+                d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationFH:
+                d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreRL:
+                d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreAP:
+                d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreFH:
+                d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kSliceOrient: {
+                char orientStr[kDICOMStr];
+                orientStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr
+                dcmStr (lLength, &buffer[lPos], orientStr);
+                if (toupper(orientStr[0])== 'S')
+                    d.sliceOrient = kSliceOrientSag; //sagittal
+                else if (toupper(orientStr[0])== 'C')
+                    d.sliceOrient = kSliceOrientCor; //coronal
+                else
+                    d.sliceOrient = kSliceOrientTra; //transverse (axial)
+                break; }
+            case	kDiffusionBFactor:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions
+                    if (d.CSA.numDti == 2) { //First time we know that this is a 4D DTI dataset
+                        //d.dti4D = (TDTI *)malloc(kMaxDTI4D * sizeof(TDTI));
+                        dti4D->S[0].V[0] = d.CSA.dtiV[0];
+                        dti4D->S[0].V[1] = d.CSA.dtiV[1];
+                        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];
+                    /*if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                       d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
+                }
+                break;
+            case    kDiffusionDirectionRL:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    d.CSA.dtiV[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                    if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D))
+                        dti4D->S[d.CSA.numDti-1].V[1] = d.CSA.dtiV[1];
+                }
+                /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
+                break;
+            case kDiffusionDirectionAP:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    d.CSA.dtiV[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                    if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D))
+                        dti4D->S[d.CSA.numDti-1].V[2] = d.CSA.dtiV[2];
+                }
+                /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
+                break;
+            case	kDiffusionDirectionFH:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    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]);
+                }
+                /*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);*/
+                //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI
+                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
+                break;
+            case 	kCSAImageHeaderInfo:
+                readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, dti4D);
+                d.isHasPhase = d.CSA.isPhaseMap;
+                break;
+                //case kObjectGraphics:
+                //    printf("---->%d,",lLength);
+                //    break;
+            case 	kRealWorldIntercept:
+                if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value
+                    d.intenIntercept = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kRealWorldSlope:
+                if (isSameFloat(1.0, d.intenScale))  //give precedence to standard value
+                    d.intenScale = dcmFloatDouble(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case kDiffusionBFactorGE :
+                if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0] =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            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
+                    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 	kOrientationACR : //use in emergency if kOrientation is not present!
+                if (!isOrient) dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient);
+                break;
+            case 	kOrientation :
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient);
+                isOrient = true;
+                break;
+            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
+                    d.imageStart = (int)lPos;
+                //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) {
+                    lLength = 0;
+                    isEncapsulatedData = true;
+                }
+
+                isIconImageSequence = false;
+                break;
+            case 	kImageStartFloat:
+                d.isFloat = true;
+                if (!isIconImageSequence) //do not exit for proprietary thumbnails
+                    d.imageStart = (int)lPos;
+                isIconImageSequence = false;
+                break;
+            case 	kImageStartDouble:
+                printf("WARNING: 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;
+                isIconImageSequence = false;
+                break;
+
+        } //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);
+        //#endif
+        lPos = lPos + (lLength);
+    }
+    free (buffer);
+    d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime);
+    //printf("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))
+        d.locationsInAcquisition = imagesInAcquisition; //e.g. if 72 slices acquired but interpolated as 144
+    if ((d.manufacturer == kMANUFACTURER_GE) && (d.locationsInAcquisition == 0))
+        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!
+    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");
+        d.xyzMM[2] = d.xyzMM[1];
+    }
+    if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) {
+        printf("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");
+        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);
+
+
+    //int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc
+    //if (d.seriesNum > 100)
+    //    kEchoMult = 10; //For Philips data Saved as Series 101,201,301... save 2nd echo as 111, 3rd as 121, etc
+    //if (coilNum > 0) //segment images with multiple coils
+    //    d.seriesNum = d.seriesNum + (100*coilNum);
+    //if (d.echoNum > 1) //segment images with multiple echoes
+    //    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);
+        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");
+    }
+    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.
+    if ((d.manufacturer == kMANUFACTURER_GE) && (strlen(d.seriesDescription) > 1)) //GE uses a generic session name here: do not overwrite kProtocolNameGE
+		strcpy(d.protocolName, d.seriesDescription);
+    if ((strlen(d.protocolName) < 1) && (strlen(d.seriesDescription) > 1))
+		strcpy(d.protocolName, d.seriesDescription);
+    if ((strlen(d.protocolName) < 1) && (strlen(d.sequenceName) > 1))
+		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);
+	//     	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);
+	//     	else
+	//     		printf("Serious error: spatial orientation ambiguous (tag 0020,0037 not found): %s\n", fname);
+	//     }
+    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, [...]
+        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]);
+    }
+    if (d.CSA.numDti >= kMaxDTI4D) {
+        printf("Error: unable to convert DTI [increase kMaxDTI4D]\n");
+        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;
+        }
+    }
+    return d;
+} // readDICOM()
+
+struct TDICOMdata readDICOM(char * fname) {
+    TDTI4D unused;
+    return readDICOMv(fname, false, 0, &unused);
+} // readDICOM()
+
+
diff --git a/console/nii_dicom.h b/console/nii_dicom.h
new file mode 100644
index 0000000..e087d38
--- /dev/null
+++ b/console/nii_dicom.h
@@ -0,0 +1,84 @@
+#include <stdbool.h>
+#include <string.h>
+#include "nifti1.h"
+
+#ifndef MRIpro_nii_dcm_h
+
+#define MRIpro_nii_dcm_h
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#ifdef myEnableJasper
+    #define kDCMvers "1Nov2016j" //JASPER for JPEG2000
+#else
+	#ifdef myDisableOpenJPEG
+    #define kDCMvers "1Nov2016" //no decompressor
+	#else
+    #define kDCMvers "1Nov2016o" //OPENJPEG for JPEG2000
+    #endif
+#endif
+
+static const int kMaxDTI4D = 4000; //#define kMaxDTIv  4000
+#define kDICOMStr  31
+#define kMANUFACTURER_UNKNOWN  0
+#define kMANUFACTURER_SIEMENS  1
+#define kMANUFACTURER_GE  2
+#define kMANUFACTURER_PHILIPS  3
+#define kMANUFACTURER_TOSHIBA  4
+static const int kSliceOrientUnknown = 0;
+static const int kSliceOrientTra = 1;
+static const int kSliceOrientSag = 2;
+static const int kSliceOrientCor = 3;
+static const int kSliceOrientMosaicNegativeDeterminant = 4;
+static const int kCompressNone = 0;
+static const int kCompressYes = 1;
+static const int kCompressC3 = 2; //obsolete JPEG lossless
+static const int kCompress50 = 3; //obsolete JPEG lossy
+    struct TDTI {
+        float V[4];
+        float sliceTiming;
+    };
+    struct TDTI4D {
+        struct TDTI S[kMaxDTI4D];
+    };
+
+    struct TCSAdata {
+    	bool isPhaseMap;
+        float dtiV[4], sliceNormV[4], bandwidthPerPixelPhaseEncode, sliceMeasurementDuration;
+        int numDti, multiBandFactor, sliceOrder, slice_start, slice_end, mosaicSlices,protocolSliceNumber1,phaseEncodingDirectionPositive;
+    };
+    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],
+        double dateTime, acquisitionTime, acquisitionDate;
+        bool 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];
+        struct TCSAdata CSA;
+    };
+
+    size_t nii_ImgBytes(struct nifti_1_header hdr);
+    struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D);
+    struct TDICOMdata readDICOM(char * fname);
+    struct TDICOMdata clear_dicom_data();
+    unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h);
+    unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h);
+    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)
+
+    int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h);
+    //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);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp
new file mode 100755
index 0000000..dd874f6
--- /dev/null
+++ b/console/nii_dicom_batch.cpp
@@ -0,0 +1,2347 @@
+//#define myNoSave //do not save images to disk
+#ifdef _MSC_VER
+	#include <direct.h>
+	#define getcwd _getcwd
+	#define chdir _chrdir
+	#include "io.h"
+	//#include <math.h>
+    #define MiniZ
+#else
+	#include <unistd.h>
+    #ifdef myDisableMiniZ
+   		#undef MiniZ
+    #else
+    	#define MiniZ
+    #endif
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+//#include "nii_foreign.h"
+#endif
+#ifndef myDisableZLib
+    #ifdef MiniZ
+        #include "miniz.c"  //single file clone of libz
+    #else
+        #include <zlib.h>
+    #endif
+#endif
+#ifdef myUseCOut
+	#include <iostream>
+#endif
+#include "nifti1_io_core.h"
+#include "nifti1.h"
+#include "nii_dicom_batch.h"
+#include "nii_dicom.h"
+#include "tinydir.h"
+#include <ctype.h> //toupper
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#ifdef myEnableOtsu
+	#include "nii_ostu_ml.h" //provide better brain crop, but artificially reduces signal variability in air
+#endif
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#include "nii_ortho.h"
+#if defined(_WIN64) || defined(_WIN32)
+    #include <windows.h> //write to registry
+#endif
+#ifndef M_PI
+	#define M_PI 3.14159265358979323846
+#endif
+#if defined(_WIN64) || defined(_WIN32)
+	const char kPathSeparator ='\\';
+	const char kFileSep[2] = "\\";
+#else
+	const char kPathSeparator ='/';
+	const char kFileSep[2] = "/";
+#endif
+
+struct TDCMsort {
+    uint64_t indx, img;
+};
+
+struct TSearchList {
+    unsigned long numItems, maxItems;
+    char **str;
+};
+
+void dropFilenameFromPath(char *path) { //
+   const char *dirPath = strrchr(path, '/'); //UNIX
+   if (dirPath == 0)
+      dirPath = strrchr(path, '\\'); //Windows
+    if (dirPath == NULL) {
+        strcpy(path,"");
+    } else
+        path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory
+    if (strlen(path) == 0) { //file name did not specify path, assume relative path and return current working directory
+    	//strcat (path,"."); //relative path - use cwd <- not sure if this works on Windows!
+    	char cwd[1024];
+   		getcwd(cwd, sizeof(cwd));
+   		strcat (path,cwd);
+    }
+}
+
+void dropTrailingFileSep(char *path) { //
+   size_t len = strlen(path) - 1;
+   if (len <= 0) return;
+   if (path[len] == '/')
+   	path[len] = '\0';
+   else if (path[len] == '\\')
+   	path[len] = '\0';
+}
+
+
+void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2'
+{
+    const char *filename = strrchr(path, '/'); //UNIX
+    if (filename == 0) {
+       filename = strrchr(path, '\\'); //Windows
+       if (filename == NULL) filename = strrchr(path, ':'); //Windows
+     }
+    //const char *filename = strrchr(path, kPathSeparator); //x
+    if (filename == NULL) {//no path separator
+        strcpy(pathParent,path);
+        return;
+    }
+    filename++;
+    strcpy(pathParent,filename);
+}
+
+bool is_fileexists(const char * filename) {
+    FILE * fp = NULL;
+    if ((fp = fopen(filename, "r"))) {
+        fclose(fp);
+        return true;
+    }
+    return false;
+}
+
+#ifndef S_ISDIR
+	#define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+	#define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
+#endif
+
+bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include <sys/stat.h>
+    struct stat buf;
+    stat(path, &buf);
+    return S_ISREG(buf.st_mode);
+} //is_file()
+
+bool is_exe(const char* path) { //requires #include <sys/stat.h>
+    struct stat buf;
+    if (stat(path, &buf) != 0) return false; //file does not eist
+    if (!S_ISREG(buf.st_mode)) return false; //not regular file, e.g. '..'
+    return (buf.st_mode & 0111) ;
+    //return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) );
+} //is_exe()
+
+int is_dir(const char *pathname, int follow_link) {
+    struct stat s;
+    if ((NULL == pathname) || (0 == strlen(pathname)))
+        return 0;
+    int err = stat(pathname, &s);
+    if(-1 == err)
+        return 0; /* does not exist */
+    else {
+        if(S_ISDIR(s.st_mode)) {
+           return 1; /* it's a dir */
+        } else {
+            return 0;/* exists but is no dir */
+        }
+    }
+}// is_dir()
+
+void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
+    //0018,1312 phase encoding is either in row or column direction
+    //0043,1039 (or 0043,a039). b value (as the first number in the string).
+    //0019,10bb (or 0019,a0bb). phase diffusion direction
+    //0019,10bc (or 0019,a0bc). frequency diffusion direction
+    //0019,10bd (or 0019,a0bd). slice diffusion direction
+    //These directions are relative to freq,phase,slice, so although no
+    //transformations are required, you need to check the direction of the
+    //phase encoding. This is in DICOM message 0018,1312. If this has value
+    //COL then if swap the x and y value and reverse the sign on the z value.
+    //If the phase encoding is not COL, then just reverse the sign on the x value.
+    if (d->manufacturer != kMANUFACTURER_GE) return;
+    if (d->CSA.numDti < 1) return;
+    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;
+    }
+    bool col = false;
+    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");
+        return;
+    }
+    if (abs(sliceDir) != 3)
+        printf("Warning: 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)
+		flp = setiVec3(1, 1, 0); //SAGITTAL
+	else if (abs(sliceDir) == 2)
+		flp = setiVec3(0, 1, 1); //CORONAL
+	else if (abs(sliceDir) == 3)
+		flp = setiVec3(0, 0, 1); //AXIAL
+	else
+		printf("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]);
+	if (!col)
+		printf(" 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])
+                          + (vx[i].V[3]*vx[i].V[3]));
+        if ((vx[i].V[0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                vx[i].V[v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }
+        if (!col) { //rows need to be swizzled
+        	//see Stanford dataset Ax_DWI_Tetrahedral_7 unable to resolve between possible solutions
+        	// http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging
+            float swap = vx[i].V[1];
+            vx[i].V[1] = vx[i].V[2];
+            vx[i].V[2] = swap;
+            vx[i].V[2] = -vx[i].V[2]; //because of transpose?
+        }
+		for (int v = 0; v < 3; v++)
+			if (flp.v[v] == 1)
+				vx[i].V[v+1] = -vx[i].V[v+1];
+		vx[i].V[2] = -vx[i].V[2]; //we read out Y-direction opposite order as dicm2nii, see also opts.isFlipY
+    }
+    //These next lines are only so files appear identical to old versions of dcm2niix:
+    //  dicm2nii and dcm2niix generate polar opposite gradient directions.
+    //  this does not matter, since intensity is the normal of the gradient vector.
+    for (int i = 0; i < d->CSA.numDti; i++)
+    	for (int v = 1; v < 4; v++)
+    		vx[i].V[v] = -vx[i].V[v];
+    //These next lines convert any "-0" values to "0"
+    for (int i = 0; i < d->CSA.numDti; i++)
+    	for (int v = 1; v < 4; v++)
+    		if (isSameFloat(vx[i].V[v],-0))
+    			vx[i].V[v] = 0.0f;
+}// geCorrectBvecs()
+
+void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
+    //see Matthew Robson's  http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m
+    //convert DTI vectors from scanner coordinates to image frame of reference
+    //Uses 6 orient values from ImageOrientationPatient  (0020,0037)
+    // requires PatientPosition 0018,5100 is HFS (head first supine)
+    if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return;
+    if (d->CSA.numDti < 1) return;
+    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
+        return;
+    }
+    vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]);
+    vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]);
+    vec3 slice_vector = crossProduct(read_vector ,phase_vector);
+    read_vector = nifti_vect33_norm(read_vector);
+    phase_vector = nifti_vect33_norm(phase_vector);
+    slice_vector = nifti_vect33_norm(slice_vector);
+    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])
+                          + (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);
+            //for (int v= 0; v < 4; v++)
+            //    vx[i].V[v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }//if bvalue=0
+        vec3 bvecs_old =setVec3(vx[i].V[1],vx[i].V[2],vx[i].V[3]);
+        vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) );
+        bvecs_new = nifti_vect33_norm(bvecs_new);
+        vx[i].V[1] = bvecs_new.v[0];
+        vx[i].V[2] = -bvecs_new.v[1];
+        vx[i].V[3] = bvecs_new.v[2];
+        if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) vx[i].V[2] = -vx[i].V[2];
+        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");
+}// siemensPhilipsCorrectBvecs()
+
+bool isNanPosition(struct TDICOMdata d) { //in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag
+    if (isnan(d.patientPosition[1])) return true;
+    if (isnan(d.patientPosition[2])) return true;
+    if (isnan(d.patientPosition[3])) return true;
+    return false;
+}// isNanPosition()
+
+bool isSamePosition(struct TDICOMdata d, struct TDICOMdata d2){
+    if ( isNanPosition(d) ||  isNanPosition(d2)) return false;
+    if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false;
+    if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false;
+    if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false;
+    return true;
+}// isSamePosition()
+
+void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct nifti_1_header *h, char * dcmname) {
+	if (!opts.isCreateText) return;
+	char txtname[2048] = {""};
+	strcpy (txtname,pathoutname);
+    strcat (txtname,".txt");
+    //printf("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",
+      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],
+            d.bitsAllocated, dcmname);
+    fclose(fp);
+}// nii_SaveText()
+
+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
+// sidecar JSON file (with the same  filename as the .nii.gz file, but with .json extension).
+// we will use %g for floats since exponents are allowed
+// we will not set the locale, so decimal separator is always a period, as required
+//  https://www.ietf.org/rfc/rfc4627.txt
+	if (!opts.isCreateBIDS) return;
+	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) {
+		case kMANUFACTURER_SIEMENS:
+			fprintf(fp, "\t\"Manufacturer\": \"Siemens\",\n" );
+			break;
+		case kMANUFACTURER_GE:
+			fprintf(fp, "\t\"Manufacturer\": \"GE\",\n" );
+			break;
+		case kMANUFACTURER_PHILIPS:
+			fprintf(fp, "\t\"Manufacturer\": \"Philips\",\n" );
+			break;
+		case kMANUFACTURER_TOSHIBA:
+			fprintf(fp, "\t\"Manufacturer\": \"Toshiba\",\n" );
+			break;
+	};
+	fprintf(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName );
+	if (strlen(d.imageType) > 0) {
+		fprintf(fp, "\t\"ImageType\": [\"");
+		bool isSep = false;
+		for (int i = 0; i < strlen(d.imageType); i++) {
+			if (d.imageType[i] != '_') {
+				if (isSep)
+		  			fprintf(fp, "\", \"");
+				isSep = false;
+				fprintf(fp, "%c", d.imageType[i]);
+			} else
+				isSep = true;
+		}
+		fprintf(fp, "\"],\n");
+	}
+	//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) 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.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.TR > 0.0) fprintf(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0 );
+    if ((d.CSA.bandwidthPerPixelPhaseEncode > 0.0) &&  (h->dim[2] > 0) && (h->dim[1] > 0)) {
+		float dwellTime = 0;
+		if (d.phaseEncodingRC =='C')
+			dwellTime =  1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+		else
+			dwellTime =  1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
+		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++) {
+			if (dti4D->S[i].sliceTiming >= 0.0){
+			  if (!first)
+				  fprintf(fp, ",\n");
+				else
+				  first = 0;
+				fprintf(fp, "\t\t%g", dti4D->S[i].sliceTiming / 1000.0 );
+			}
+		}
+		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");
+    fprintf(fp, "}\n");
+    fclose(fp);
+}// nii_SaveBIDS()
+
+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)
+    //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;
+    TDTI * vx = NULL;
+    if (numDti > 2) {
+        vx = (TDTI *)malloc(numDti * sizeof(TDTI));
+        for (int i = 0; i < numDti; i++) //for each direction
+            for (int v = 0; v < 4; v++) //for each vector+B-value
+                    vx[i].V[v] = dti4D->S[i].V[v];
+    } else { //if (numDti == 1) {//extract DTI from different slices
+        vx = (TDTI *)malloc(nConvert * sizeof(TDTI));
+        numDti = 0;
+        for (int i = 0; i < nConvert; i++) { //for each image
+            if ((dcmList[indx0].CSA.mosaicSlices > 1)  || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) {
+                //if (numDti < kMaxDTIv)
+                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;
+    }
+    bool bValueVaries = false;
+    for (int i = 1; i < numDti; i++) //check if all bvalues match first volume
+        if (vx[i].V[0] != vx[0].V[0]) bValueVaries = true;
+    if (!bValueVaries) {
+        bool bVecVaries = false;
+        for (int i = 1; i < numDti; i++) {//check if all bvalues match first volume
+            if (vx[i].V[1] != vx[0].V[1]) bVecVaries = true;
+            if (vx[i].V[2] != vx[0].V[2]) bVecVaries = true;
+            if (vx[i].V[3] != vx[0].V[3]) bVecVaries = true;
+        }
+        if (!bVecVaries) {
+                free(vx);
+                return false;
+        }
+        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]);
+        free(vx);
+        return false;
+    }
+    int firstB0 = -1;
+    for (int i = 0; i < numDti; i++) //check if all bvalues match first volume
+        if (isSameFloat(vx[i].V[0],0) ) {
+            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 (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 --;
+        } //
+        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]);
+
+        }*/
+    }
+    // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution
+    geCorrectBvecs(&dcmList[indx0],sliceDir, vx);
+    siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir, vx);
+    if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction
+        for (int i = 0; i < (numDti); i++) {
+            if (fabs(vx[i].V[2]) > FLT_EPSILON)
+                vx[i].V[2] = -vx[i].V[2];
+        } //for each direction
+    } //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],
+                   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]);
+    char txtname[2048] = {""};
+    strcpy (txtname,pathoutname);
+    strcat (txtname,".bval");
+    //printf("Saving DTI %s\n",txtname);
+    FILE *fp = fopen(txtname, "w");
+    if (fp == NULL) {
+        free(vx);
+        return numFinalADC;
+    }
+    for (int i = 0; i < (numDti-1); i++) {
+        if (opts.isCreateBIDS) {
+            fprintf(fp, "%g ", vx[i].V[0]);
+        } else {
+            fprintf(fp, "%g\t", vx[i].V[0]);
+        }
+	}
+    fprintf(fp, "%g\n", vx[numDti-1].V[0]);
+    fclose(fp);
+    strcpy(txtname,pathoutname);
+    strcat (txtname,".bvec");
+    //printf("Saving DTI %s\n",txtname);
+    fp = fopen(txtname, "w");
+    if (fp == NULL) {
+        free(vx);
+        return numFinalADC;
+    }
+    for (int v = 1; v < 4; v++) {
+        for (int i = 0; i < (numDti-1); i++) {
+            if (opts.isCreateBIDS) {
+                fprintf(fp, "%g ", vx[i].V[v]);
+            } else {
+                fprintf(fp, "%g\t", vx[i].V[v]);
+            }
+        }
+        fprintf(fp, "%g\n", vx[numDti-1].V[v]);
+    }
+    fclose(fp);
+    free(vx);
+    return numFinalADC;
+}// nii_SaveDTI()
+
+float sqr(float v){
+    return v*v;
+}// sqr()
+
+float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) {
+    //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices
+    if ( isNanPosition(d1) ||  isNanPosition(d2))
+        return d1.xyzMM[3];
+    float tilt = 1.0;
+    if (d1.gantryTilt != 0)
+        tilt = (float) cos(d1.gantryTilt  * M_PI/180); //for CT scans with gantry tilt, we need to compute distance between slices, not distance along bed
+    return tilt * sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+
+                sqr(d1.patientPosition[2]-d2.patientPosition[2])+
+                sqr(d1.patientPosition[3]-d2.patientPosition[3]));
+} //intersliceDistance()
+
+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
+	TDCMsort * dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
+#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);
+            dcmSort[k] = dcmSortIn[i];
+            i++;
+        }
+#ifdef _MSC_VER
+	free(dcmSortIn);
+#endif
+} //swapDim3Dim4()
+
+bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){
+    //detect whether some DICOM images report different intensity scaling
+    //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor.
+    // since NIfTI provides a single scaling factor for each file, these images require special consideration
+    if (nConvert < 2) return false;
+    bool iVaries = false;
+    float iScale = dcmList[dcmSort[0].indx].intenScale;
+    float iInter = dcmList[dcmSort[0].indx].intenIntercept;
+    for (int i = 1; i < nConvert; i++) { //stack additional images
+        uint64_t indx = dcmSort[i].indx;
+        if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true;
+        if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true;
+    }
+    return iVaries;
+} //intensityScaleVaries()
+
+/*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) {
+ //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+ //  see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard
+ if (hdr->datatype != DT_RGB24) return bImg;
+ int dim3to7 = 1;
+ for (int i = 3; i < 8; i++)
+ if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+ int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
+ int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
+ //Byte bImg[ bSz ];
+ //[img getBytes:&bImg length:bSz];
+ unsigned char slice24[sliceBytes24];
+ int sliceOffsetR = 0;
+ for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+ memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ } //for each slice
+ return bImg;
+ } //nii_ImgBytes()*/
+
+bool niiExists(const char*pathoutname) {
+    char niiname[2048] = {""};
+    strcat (niiname,pathoutname);
+    strcat (niiname,".nii");
+    if (is_fileexists(niiname)) return true;
+    char gzname[2048] = {""};
+    strcat (gzname,pathoutname);
+    strcat (gzname,".nii.gz");
+    if (is_fileexists(gzname)) return true;
+    return false;
+} //niiExists()
+
+#ifndef W_OK
+#define W_OK 2 /* write mode check */
+#endif
+
+int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) {
+    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) {
+            	#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
+                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
+            }
+        }
+     }
+    char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time
+    strcpy(inname, opts.filename);
+    char outname[1024] = {""};
+    char newstr[256];
+    if (strlen(inname) < 1) {
+        strcpy(inname, "T%t_N%n_S%s");
+    }
+    int start = 0;
+    int pos = 0;
+    bool isCoilReported = false;
+    bool isEchoReported = false;
+    while (pos < strlen(inname)) {
+        if (inname[pos] == '%') {
+            if (pos > start) {
+                strncpy(&newstr[0], &inname[0] + start, pos - start);
+                newstr[pos - start] = '\0';
+                strcat (outname,newstr);
+            }
+            pos++; //extra increment: skip both % and following character
+            char f = 'P';
+            if (pos < strlen(inname)) f = toupper(inname[pos]);
+        	if ((f == 'A') && (dcm.coilNum > 0)) {
+        		isCoilReported = true;
+                sprintf(newstr, "%02d", dcm.coilNum);
+                strcat (outname,newstr);
+            }
+        	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 == 'M') {
+                if (dcm.manufacturer == kMANUFACTURER_GE)
+                    strcat (outname,"GE");
+                else if (dcm.manufacturer == kMANUFACTURER_TOSHIBA)
+                    strcat (outname,"To");
+                else if (dcm.manufacturer == kMANUFACTURER_PHILIPS)
+                    strcat (outname,"Ph");
+                else if (dcm.manufacturer == kMANUFACTURER_SIEMENS)
+                    strcat (outname,"Si");
+                else
+                    strcat (outname,"NA"); //manufacturer name not available
+            }
+            if (f == 'N')
+                strcat (outname,dcm.patientName);
+            if (f == 'P')
+                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);
+            }
+            if (f == 'T') {
+                sprintf(newstr, "%0.0f", dcm.dateTime);
+                strcat (outname,newstr);
+            }
+			if (f == 'U') {
+				#ifdef mySegmentByAcq
+				sprintf(newstr, "%d", dcm.acquNum);
+				strcat (outname,newstr);
+				#else
+    			printf("Warning: ignoring '%%f' in output filename (recompile to segment by acquisition)\n");
+    			#endif
+			}
+            if (f == 'Z')
+                strcat (outname,dcm.sequenceName);
+            start = pos + 1;
+        } //found a % character
+        pos++;
+    } //for each character in input
+    if (!isCoilReported && (dcm.coilNum > 1)) {
+        sprintf(newstr, "_c%d", dcm.coilNum);
+        strcat (outname,newstr);
+    }
+    if (!isEchoReported && (dcm.echoNum > 1)) {
+        sprintf(newstr, "_e%d", dcm.echoNum);
+        strcat (outname,newstr);
+    }
+    if (dcm.isHasPhase)
+    	strcat (outname,"_ph"); //manufacturer name not available
+    if (pos > start) { //append any trailing characters
+        strncpy(&newstr[0], &inname[0] + start, pos - start);
+        newstr[pos - start] = '\0';
+        strcat (outname,newstr);
+    }
+    if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName");
+    if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file
+    //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+    for (int pos = 0; pos<strlen(outname); pos ++)
+        if ((outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':')
+            || (outname[pos] == '"') || (outname[pos] == '\\') || (outname[pos] == '/')
+            || (outname[pos] == '^')
+            || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?'))
+            outname[pos] = '_';
+    //printf("outname=*%s* %d %d\n", outname, pos,start);
+    char baseoutname[2048] = {""};
+    strcat (baseoutname,pth);
+    char appendChar[2] = {"a"};
+    appendChar[0] = kPathSeparator;
+    if (pth[strlen(pth)-1] != kPathSeparator)
+        strcat (baseoutname,appendChar);
+    strcat (baseoutname,outname);
+    char pathoutname[2048] = {""};
+    strcat (pathoutname,baseoutname);
+    int i = 0;
+    while (niiExists(pathoutname) && (i < 26)) {
+        strcpy(pathoutname,baseoutname);
+        appendChar[0] = 'a'+i;
+        strcat (pathoutname,appendChar);
+        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
+        return EXIT_FAILURE;
+    }
+    //printf("-->%s\n",pathoutname); return EXIT_SUCCESS;
+    //printf("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();
+    strcpy(opts.indirParent,"myFolder");
+    char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"};
+    nii_createFilename(dcm, niiFilenameBase, opts) ;
+    strcpy(niiFilename,"Example output filename: '");
+    strcat(niiFilename,niiFilenameBase);
+    if (opts.isGz)
+        strcat(niiFilename,".nii.gz'");
+    else
+        strcat(niiFilename,".nii'");
+}// nii_createDummyFilename()
+
+#ifndef myDisableZLib
+
+#ifndef MiniZ
+unsigned long mz_compressBound(unsigned long source_len) {
+	return compressBound(source_len);
+}
+
+unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_len) {
+    return crc32(crc, ptr, (uInt) buf_len);
+}
+#endif
+
+void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src_buffer, unsigned long src_len) {
+    //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] = {""};
+    strcpy (fname,baseName);
+    strcat (fname,".nii.gz");
+    unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad
+    unsigned long cmp_len = mz_compressBound(src_len+hdrPadBytes);
+    unsigned char *pCmp = (unsigned char *)malloc(cmp_len);
+    z_stream strm;
+    strm.total_in = 0;
+    strm.total_out = 0;
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    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) {
+        free(pCmp);
+        return;
+    }
+    //add header
+    unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes);
+    pHdr[hdrPadBytes-1] = 0; pHdr[hdrPadBytes-2] = 0; pHdr[hdrPadBytes-3] = 0; pHdr[hdrPadBytes-4] = 0;
+    memcpy(pHdr,&hdr, sizeof(hdr));
+    strm.avail_in = (unsigned int)hdrPadBytes; // size of input
+	strm.next_in = (uint8_t *)pHdr; // input header -- TPX  strm.next_in = (Bytef *)pHdr; uint32_t
+    deflate(&strm, Z_NO_FLUSH);
+    //add image
+    strm.avail_in = (unsigned int)src_len; // size of input
+	strm.next_in = (uint8_t *)src_buffer; // input image -- TPX strm.next_in = (Bytef *)src_buffer;
+    deflate(&strm, Z_FINISH); //Z_NO_FLUSH;
+    //finish up
+    deflateEnd(&strm);
+    unsigned long file_crc32 = mz_crc32(0L, Z_NULL, 0);
+    file_crc32 = mz_crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes);
+    file_crc32 = mz_crc32(file_crc32, src_buffer, (unsigned int)src_len);
+    cmp_len = strm.total_out;
+    if (cmp_len <= 0) {
+        free(pCmp);
+        free(src_buffer);
+        return;
+    }
+    FILE *fileGz = fopen(fname, "wb");
+    if (!fileGz) {
+        free(pCmp);
+        free(src_buffer);
+        return;
+    }
+    //write header http://www.gzip.org/zlib/rfc-gzip.html
+    fputc((char)0x1f, fileGz); //ID1
+    fputc((char)0x8b, fileGz); //ID2
+    fputc((char)0x08, fileGz); //CM - use deflate compression method
+    fputc((char)0x00, fileGz); //FLG - no addition fields
+    fputc((char)0x00, fileGz); //MTIME0
+    fputc((char)0x00, fileGz); //MTIME1
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //XFL
+    fputc((char)0xff, fileGz); //OS
+    //write Z-compressed data
+    fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header (typically 0x789C) and 4 bytes tail (ADLER 32)
+    //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order
+    fputc((unsigned char)(file_crc32), fileGz);
+    fputc((unsigned char)(file_crc32 >> 8), fileGz);
+    fputc((unsigned char)(file_crc32 >> 16), fileGz);
+    fputc((unsigned char)(file_crc32 >> 24), fileGz);
+    fputc((unsigned char)(strm.total_in), fileGz);
+    fputc((unsigned char)(strm.total_in >> 8), fileGz);
+    fputc((unsigned char)(strm.total_in >> 16), fileGz);
+    fputc((unsigned char)(strm.total_in >> 24), fileGz);
+    fclose(fileGz);
+    free(pCmp);
+    free(pHdr);
+} //writeNiiGz()
+#endif
+
+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);
+    #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);
+        return EXIT_SUCCESS;
+    }
+    #endif
+    char fname[2048] = {""};
+    strcpy (fname,niiFilename);
+    strcat (fname,".nii");
+    FILE *fp = fopen(fname, "wb");
+    if (!fp) return EXIT_FAILURE;
+    fwrite(&hdr, sizeof(hdr), 1, fp);
+    uint32_t pad = 0;
+    fwrite(&pad, sizeof( pad), 1, fp);
+    fwrite(&im[0], imgsz, 1, fp);
+    fclose(fp);
+    if ((opts.isGz) &&  (strlen(opts.pigzname)  > 0) ) {
+    	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 ':\'
+        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)
+    		DWORD exitCode;
+   		PROCESS_INFORMATION ProcessInfo = {0};
+   		STARTUPINFO startupInfo= {0};
+   		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);
+        		WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+        		CloseHandle(ProcessInfo.hThread);
+        		CloseHandle(ProcessInfo.hProcess);
+    		} else
+    			printf("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
+    }
+    return EXIT_SUCCESS;
+}// nii_saveNII()
+
+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;
+    int nVol = 1;
+    for (int i = 4; i < 8; i++) {
+        if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i];
+        hdr1.dim[i] = 0;
+    }
+    hdr1.dim[0] = 3; //save as 3D file
+    size_t imgsz = nii_ImgBytes(hdr1);
+    size_t pos = 0;
+    char fname[2048] = {""};
+    char zeroPad[1024] = {""};
+	double fnVol = nVol;
+	int zeroPadLen = (1 + log10( fnVol));
+    sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen);
+    for (int i = 1; i <= nVol; i++) {
+        sprintf(fname,zeroPad,niiFilename,i);
+        if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE;
+        pos += imgsz;
+    }
+    return EXIT_SUCCESS;
+}// nii_saveNII3D()
+
+void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){
+    //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required...
+    if (hdr->datatype != DT_UINT16) return;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return;
+    unsigned short * img16 = (unsigned short*) img;
+    unsigned short max16 = img16[0];
+    //clock_t start = clock();
+    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");
+    else
+        hdr->datatype = DT_INT16;
+} //nii_check16bitUnsigned()
+
+int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) {
+    //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image,
+    //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect
+    uint64_t indx0 = dcmSort[0].indx;
+    if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert;
+    float prevDx = 0.0;
+    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
+            return i;
+        }
+        prevDx = dx;
+    }
+    return nConvert; //all images in sequential order
+}// siemensCtKludge()
+
+int isSameFloatT (float a, float b, float tolerance) {
+    return (fabs (a - b) <= tolerance);
+}
+
+unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hdr, unsigned char* im, struct TDCMopts opts, float * sliceMMarray, float gantryTiltDeg, int manufacturer ) {
+    //correct for gantry tilt - http://www.mathworks.com/matlabcentral/fileexchange/24458-dicom-gantry-tilt-correction
+    if (gantryTiltDeg == 0.0) return im;
+    struct nifti_1_header hdrIn = *hdr;
+    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.");
+        return im;
+    }
+    printf("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
+    // also validated with actual data...
+    if (manufacturer == kMANUFACTURER_PHILIPS) //see 'Manix' example from Osirix
+        GNTtanPx = - GNTtanPx;
+    else if ((manufacturer == kMANUFACTURER_SIEMENS) && (gantryTiltDeg > 0.0))
+        GNTtanPx = - GNTtanPx;
+    else if (manufacturer == kMANUFACTURER_GE)
+        ; //do nothing
+    else
+        if (gantryTiltDeg < 0.0) GNTtanPx = - GNTtanPx; //see Toshiba examples from John Muschelli
+    // printf("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
+    int s = hdrIn.dim[3] - 1; //top slice
+    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);
+	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) );
+	short * imOut16 = ( short*) imOut;
+	//set surrounding voxels to darkest observed value
+	int minVoxVal = imIn16[0];
+	for (int v = 0; v < (nVox2DIn * hdrIn.dim[3]); v++)
+		if (imIn16[v] < minVoxVal)
+			minVoxVal = imIn16[v];
+	for (int v = 0; v < (nVox2D * hdrIn.dim[3]); v++)
+		imOut16[v] = minVoxVal;
+	//copy skewed voxels
+	for (int s = 0; s < hdrIn.dim[3]; s++) { //for each slice
+		float sliceMM = s * hdrIn.pixdim[3];
+		if (sliceMMarray != NULL) sliceMM = sliceMMarray[s]; //variable slice thicknesses
+		//sliceMM -= mmMidZ; //adjust so tilt relative to middle slice
+		if (GNTtanPx < 0)
+			sliceMM -= maxSliceMM;
+		float Offset = GNTtanPx*sliceMM;
+		float fracHi =  ceil(Offset) - Offset; //ceil not floor since rI=r-Offset not rI=r+Offset
+		float fracLo = 1.0f - fracHi;
+		for (int r = 0; r < hdr->dim[2]; r++) { //for each row of output
+			float rI = (float)r - Offset; //input row
+			if ((rI >= 0.0) && (rI < hdrIn.dim[2])) {
+				int rLo = floor(rI);
+				int rHi = rLo + 1;
+				if (rHi >= hdrIn.dim[2]) rHi = rLo;
+				rLo = (rLo * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row below
+				rHi = (rHi * hdrIn.dim[1]) + (s * nVox2DIn); //offset to start of row above
+				int rOut = (r * hdrIn.dim[1]) + (s * nVox2D); //offset to output row
+				for (int c = 0; c < hdrIn.dim[1]; c++) { //for each row
+					imOut16[rOut+c] = round( ( ((float)imIn16[rLo+c])*fracLo) + ((float)imIn16[rHi+c])*fracHi);
+				} //for c (each column)
+			} //rI (input row) in range
+		} //for r (each row)
+	} //for s (each slice)*/
+	free(im);
+    if (sliceMMarray != NULL) return imOut; //we will save after correcting for variable slice thicknesses
+    char niiFilenameTilt[2048] = {""};
+    strcat(niiFilenameTilt,niiFilename);
+    strcat(niiFilenameTilt,"_Tilt");
+    nii_saveNII3D(niiFilenameTilt, *hdr, imOut, opts);
+    return imOut;
+}// nii_saveNII3Dtilt()
+
+int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts, float * sliceMMarray ) {
+    //convert image with unequal slice distances to equal slice distances
+    //sliceMMarray = 0.0 3.0 6.0 12.0 22.0 <- ascending distance from first slice
+    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.");
+        return EXIT_FAILURE;
+    }
+    float mn = sliceMMarray[1] - sliceMMarray[0];
+    for (int i = 1; i < hdr.dim[3]; i++) {
+    	float dx = sliceMMarray[i] - sliceMMarray[i-1];
+        //if ((dx < mn) // <- only allow consistent slice direction
+        if ((dx < mn) && (dx > 0.0)) // <- allow slice direction to reverse
+            mn = sliceMMarray[i] - sliceMMarray[i-1];
+    }
+    if (mn <= 0.0f) {
+    	printf("Unable to equalize slice distances: slice number not consistent with slice position.\n");
+    	return EXIT_FAILURE;
+    }
+    int slices = hdr.dim[3];
+    slices = (int)ceil((sliceMMarray[slices-1]-0.5*(sliceMMarray[slices-1]-sliceMMarray[slices-2]))/mn); //-0.5: fence post
+    if (slices > (hdr.dim[3] * 2)) {
+        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]);
+    if (slices < 3) return EXIT_FAILURE;
+    struct nifti_1_header hdrX = hdr;
+    hdrX.dim[3] = slices;
+    hdrX.pixdim[3] = mn;
+    if ((hdr.pixdim[3] != 0.0) && (hdr.pixdim[3] != hdrX.pixdim[3])) {
+        float Scale = hdrX.pixdim[3] / hdr.pixdim[3];
+        //to do: do I change srow_z or srow_x[2], srow_y[2], srow_z[2],
+        hdrX.srow_z[0] = hdr.srow_z[0] * Scale;
+        hdrX.srow_z[1] = hdr.srow_z[1] * Scale;
+        hdrX.srow_z[2] = hdr.srow_z[2] * Scale;
+    }
+    unsigned char *imX;
+    if (hdr.datatype == DT_INT16) {
+        short * im16 = ( short*) im;
+        imX = (unsigned char *)malloc( (nVox2D * slices)  *  2);//sizeof( short) );
+        short * imX16 = ( short*) imX;
+        for (int s=0; s < slices; s++) {
+            float sliceXmm = s * mn; //distance from first slice
+            int sliceXi = (s * nVox2D);//offset for this slice
+            int sHi = 0;
+            while ((sHi < (hdr.dim[3] - 1) ) && (sliceMMarray[sHi] < sliceXmm))
+                sHi += 1;
+            int sLo = sHi - 1;
+            if (sLo < 0) sLo = 0;
+            float mmHi = sliceMMarray[sHi];
+            float mmLo = sliceMMarray[sLo];
+            sLo = sLo * nVox2D;
+            sHi = sHi * nVox2D;
+            if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX
+                //for (int v=0; v < nVox2D; v++)
+                //    imX16[sliceXi+v] = im16[sHi+v];
+                memcpy(&imX16[sliceXi], &im16[sHi], nVox2D* sizeof(unsigned short)); //memcpy( dest, src, bytes)
+
+            } else {
+                float fracHi = (sliceXmm-mmLo)/ (mmHi-mmLo);
+                float fracLo = 1.0 - fracHi;
+                //weight between two slices
+                for (int v=0; v < nVox2D; v++)
+                    imX16[sliceXi+v] = round( ( (float)im16[sLo+v]*fracLo) + (float)im16[sHi+v]*fracHi);
+            }
+        }
+    } else {
+        if (hdr.datatype == DT_RGB24) nVox2D = nVox2D * 3;
+        imX = (unsigned char *)malloc( (nVox2D * slices)  *  2);//sizeof( short) );
+        for (int s=0; s < slices; s++) {
+            float sliceXmm = s * mn; //distance from first slice
+            int sliceXi = (s * nVox2D);//offset for this slice
+            int sHi = 0;
+            while ((sHi < (hdr.dim[3] - 1) ) && (sliceMMarray[sHi] < sliceXmm))
+                sHi += 1;
+            int sLo = sHi - 1;
+            if (sLo < 0) sLo = 0;
+            float mmHi = sliceMMarray[sHi];
+            float mmLo = sliceMMarray[sLo];
+            sLo = sLo * nVox2D;
+            sHi = sHi * nVox2D;
+            if ((mmHi == mmLo) || (sliceXmm > mmHi)) { //select only from upper slice TPX
+                memcpy(&imX[sliceXi], &im[sHi], nVox2D); //memcpy( dest, src, bytes)
+            } else {
+                float fracHi = (sliceXmm-mmLo)/ (mmHi-mmLo);
+                float fracLo = 1.0 - fracHi; //weight between two slices
+                for (int v=0; v < nVox2D; v++)
+                    imX[sliceXi+v] = round( ( (float)im[sLo+v]*fracLo) + (float)im[sHi+v]*fracHi);
+            }
+        }
+    }
+    char niiFilenameEq[2048] = {""};
+    strcat(niiFilenameEq,niiFilename);
+    strcat(niiFilenameEq,"_Eq");
+    nii_saveNII3D(niiFilenameEq, hdrX, imX, opts);
+    free(imX);
+    return EXIT_SUCCESS;
+}// nii_saveNII3Deq()
+
+void smooth1D(int num, double * im) {
+	if (num < 3) return;
+	double * src = (double *) malloc(sizeof(double)*num);
+	memcpy(&src[0], &im[0], num * sizeof(double)); //memcpy( dest, src, bytes)
+	double frac = 0.25;
+	for (int i = 1; i < (num-1); i++)
+		im[i] = (src[i-1]*frac) + (src[i]*frac*2) + (src[i+1]*frac);
+	free(src);
+}// smooth1D()
+
+void 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 ((hdr.datatype != DT_INT16) && (hdr.datatype != DT_UINT16)) {
+        printf("Only able to crop 16-bit volumes.");
+        return;
+    }
+	short * im16 = ( short*) im;
+	unsigned short * imu16 = (unsigned short*) im;
+	float kThresh = 0.09; //more than 9% of max brightness
+	#ifdef myEnableOtsu
+	kThresh = 0.0001;
+	if (hdr.datatype == DT_UINT16)
+		maskBackgroundU16 (imu16, hdr.dim[1],hdr.dim[2],hdr.dim[3], 5,2, true);
+	else
+		maskBackground16 (im16, hdr.dim[1],hdr.dim[2],hdr.dim[3], 5,2, true);
+	#endif
+    int ventralCrop = 0;
+    //find max value for each slice
+    int slices = hdr.dim[3];
+    double * sliceSums = (double *) malloc(sizeof(double)*slices);
+    double maxSliceVal = 0.0;
+    for (int i = (slices-1); i  >= 0; i--) {
+    	sliceSums[i] = 0;
+    	int sliceStart = i * nVox2D;
+    	if (hdr.datatype == DT_UINT16)
+			for (int j = 0; j < nVox2D; j++)
+				sliceSums[i] += imu16[j+sliceStart];
+    	else
+			for (int j = 0; j < nVox2D; j++)
+				sliceSums[i] += im16[j+sliceStart];
+		if (sliceSums[i] > maxSliceVal)
+    		maxSliceVal = sliceSums[i];
+    }
+    if (maxSliceVal <= 0) {
+    	free(sliceSums);
+    	return;
+    }
+    smooth1D(slices, sliceSums);
+    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;
+	}
+	/*
+	//find brightest band within 90mm of top of head
+	int ventralMaxSlice = dorsalCrop - round(90 /fabs(hdr.pixdim[3])); //brightest stripe within 90mm of apex
+    if (ventralMaxSlice < 0) ventralMaxSlice = 0;
+    int maxSlice = dorsalCrop;
+    for (int i = ventralMaxSlice; i  < dorsalCrop; i++)
+    	if (sliceSums[i] > sliceSums[maxSlice])
+    		maxSlice = i;
+	//now find
+    ventralMaxSlice = maxSlice - round(45 /fabs(hdr.pixdim[3])); //gap at least 60mm
+    if (ventralMaxSlice < 0) {
+    	free(sliceSums);
+    	return;
+    }
+    int ventralMinSlice = maxSlice - round(90/fabs(hdr.pixdim[3])); //gap no more than 120mm
+    if (ventralMinSlice < 0) ventralMinSlice = 0;
+	for (int i = (ventralMaxSlice-1); i >= ventralMinSlice; i--)
+		if (sliceSums[i] > sliceSums[ventralMaxSlice])
+			ventralMaxSlice = i;
+	//finally: find minima between these two points...
+    int minSlice = ventralMaxSlice;
+    for (int i = ventralMaxSlice; i  < maxSlice; i++)
+    	if (sliceSums[i] < sliceSums[minSlice])
+    		minSlice = i;
+    //printf("%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;
+	//FindDVCrop2
+	const double kMaxDVmm = 180.0;
+    double sliceMM = hdr.pixdim[3] * (dorsalCrop-ventralCrop);
+    if (sliceMM > kMaxDVmm) { //decide how many more ventral slices to remove
+        sliceMM = sliceMM - kMaxDVmm;
+        sliceMM = sliceMM / hdr.pixdim[3];
+        ventralCrop = ventralCrop + round(sliceMM);
+    }*/
+    const double kMaxDVmm = 169.0;
+    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);
+    struct nifti_1_header hdrX = hdr;
+    slices = dorsalCrop - ventralCrop + 1;
+    hdrX.dim[3] = slices;
+    //translate origin to account for missing slices
+    hdrX.srow_x[3] += hdr.srow_x[2]*ventralCrop;
+    hdrX.srow_y[3] += hdr.srow_y[2]*ventralCrop;
+    hdrX.srow_z[3] += hdr.srow_z[2]*ventralCrop;
+	//convert data
+    unsigned char *imX;
+	imX = (unsigned char *)malloc( (nVox2D * slices)  *  2);//sizeof( short) );
+	short * imX16 = ( short*) imX;
+	for (int s=0; s < slices; s++) {
+		int sIn = s+ventralCrop;
+		int sOut = s;
+		sOut = sOut * nVox2D;
+		sIn = sIn * nVox2D;
+		memcpy(&imX16[sOut], &im16[sIn], nVox2D* sizeof(unsigned short)); //memcpy( dest, src, bytes)
+    }
+    char niiFilenameCrop[2048] = {""};
+    strcat(niiFilenameCrop,niiFilename);
+    strcat(niiFilenameCrop,"_Crop");
+    nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts);
+    free(imX);
+    return;
+}// nii_saveCrop()
+
+int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts, struct TDTI4D *dti4D) {
+    bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList);
+    float *sliceMMarray = NULL; //only used if slices are not equidistant
+    uint64_t indx = dcmSort[0].indx;
+    uint64_t indx0 = dcmSort[0].indx;
+    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
+    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]);
+    if (nConvert > 1) {
+        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);
+        if (hdr0.dim[3] < 2) {
+            //stack volumes with multiple acquisitions
+            int nAcq = 1;
+            //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq"
+            //  nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum);
+            //therefore, the 'same position' is the most robust solution in the real world.
+            if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) {
+                nConvert = siemensCtKludge(nConvert, dcmSort,dcmList);
+            }
+            if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition;
+
+            if (nAcq < 2 ) {
+                nAcq = 0;
+                for (int i = 0; i < nConvert; i++)
+                    if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++;
+            }
+            /*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);
+                //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) ) {
+                hdr0.dim[3] = nConvert/nAcq;
+                hdr0.dim[4] = nAcq;
+                hdr0.dim[0] = 4;
+            } 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);
+                }
+            }
+            float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]);
+            bool dxVaries = false;
+            for (int i = 1; i < nConvert; i++)
+                if (!isSameFloatT(dx,intersliceDistance(dcmList[dcmSort[i-1].indx],dcmList[dcmSort[i].indx]),0.2))
+                    dxVaries = true;
+            if (hdr0.dim[4] < 2) {
+                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");
+                    for (int i = 1; i < nConvert; i++) {
+                        float dx0 = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx]);
+                        printf(" %g", dx0);
+                        sliceMMarray[i] = dx0;
+                    }
+                    printf("]\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);
+            }
+            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");
+                hdr0.dim[3] = 1;
+                hdr0.dim[4] = nConvert;
+                hdr0.dim[0] = 4;
+            }
+            dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers:
+            // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm
+            if (dx > 0) hdr0.pixdim[3] = dx;
+        } else if (hdr0.dim[4] < 2) {
+            hdr0.dim[4] = nConvert;
+            hdr0.dim[0] = 4;
+        } else {
+            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]);
+        struct nifti_1_header hdrI;
+        for (int i = 1; i < nConvert; i++) { //stack additional images
+            indx = dcmSort[i].indx;
+            //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE;
+            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
+                free(imgM);
+                free(img);
+                return EXIT_FAILURE;
+            }
+            memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz);
+            free(img);
+        }
+    }
+    char pathoutname[2048] = {""};
+    if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) {
+        free(imgM);
+        return EXIT_FAILURE;
+    }
+    if (strlen(pathoutname) <1) {
+        free(imgM);
+        return EXIT_FAILURE;
+    }
+    int sliceDir = 0;
+    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");
+		//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);
+        sliceDir = abs(sliceDir); //change this, we have flipped the image so GE DTI bvecs no longer need to be flipped!
+    }
+    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
+    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
+    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
+    else
+        fflush(stdout); //GUI buffers printf, display all results
+    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));
+    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
+#ifndef myNoSave
+    //printf(" 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);
+    else {
+        if ((numFinalADC > 0) && (hdr0.dim[4] > (numFinalADC+1))) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+            char pathoutnameADC[2048] = {""};
+            strcat(pathoutnameADC,pathoutname);
+            strcat(pathoutnameADC,"_ADC");
+            nii_saveNII(pathoutnameADC, hdr0, imgM, opts);
+            hdr0.dim[4] = hdr0.dim[4]-numFinalADC;
+        };
+        nii_saveNII(pathoutname, hdr0, imgM, opts);
+    }
+#endif
+    if (dcmList[indx0].gantryTilt != 0.0) {
+        if (dcmList[indx0].isResampled)
+            printf("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");
+    }
+    if (sliceMMarray != NULL) {
+        if (dcmList[indx0].isResampled)
+            printf("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n");
+        else
+            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()!
+    free(imgM);
+    return EXIT_SUCCESS;
+}// saveDcm2Nii()
+
+int compareTDCMsort(void const *item1, void const *item2) {
+    //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html
+    struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1;
+    struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2;
+    if (dcm1->img < dcm2->img)
+        return -1;
+    else if (dcm1->img > dcm2->img)
+        return 1;
+    return 0; //tie
+} //compareTDCMsort()
+
+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 (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 (fabs (a - b) <= 0.0001);
+}
+
+struct TWarnings { //generate a warning only once per set
+        bool acqNumVaries, bitDepthVaries, dateTimeVaries, echoVaries, coilVaries, nameVaries, orientVaries;
+};
+
+TWarnings setWarnings() {
+	TWarnings r;
+	r.acqNumVaries = false;
+	r.bitDepthVaries = false;
+	r.dateTimeVaries = false;
+	r.echoVaries = false;
+	r.coilVaries = false;
+	r.nameVaries = false;
+	r.orientVaries = false;
+	return r;
+}
+
+bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSameSeries,struct TWarnings* warnings) {
+    //returns true if d1 and d2 should be stacked together as a single output
+    if (!d1.isValid) return false;
+    if (!d2.isValid) return false;
+	if (d1.seriesNum != d2.seriesNum) return false;
+	#ifdef mySegmentByAcq
+    if (d1.acquNum != d2.acquNum) return false;
+    #else
+    if (d1.acquNum != d2.acquNum) {
+        if (!warnings->acqNumVaries)
+        	printf("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");
+        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);
+    	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 );
+        warnings->echoVaries = true;
+        return false;
+    }
+    if (d1.coilNum != d2.coilNum) {
+        if (!warnings->coilVaries)
+        	printf("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");
+        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",
+               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;
+        return false;
+    }
+    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);
+        return 0;
+    }
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc( sizeof(struct  TDICOMdata));
+    struct TDTI4D dti4D;
+    struct TSearchList nameList;
+    nameList.maxItems = 1; // larger requires more memory, smaller more passes
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file
+    nameList.numItems = 0;
+    nameList.str[nameList.numItems]  = (char *)malloc(strlen(filename)+1);
+    strcpy(nameList.str[nameList.numItems],filename);
+    nameList.numItems++;
+    struct TDCMsort dcmSort[1];
+    dcmSort[0].indx = 0;
+    dcmSort[0].img = ((uint64_t)dcmList[0].seriesNum << 32) + dcmList[0].imageNum;
+    dcmList[0].converted2NII = 1;
+    dcmList[0] = readDICOMv(nameList.str[0], opts->isVerbose, opts->compressFlag, &dti4D); //ignore compile warning - memory only freed on first of 2 passes
+    return saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D);
+}// singleDICOM()
+
+void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) {
+    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);
+        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...
+            ;
+        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");
+            }
+            nameList->numItems++;
+            //printf("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
+        }
+        tinydir_next(&dir);
+    }
+    tinydir_close(&dir);
+}// searchDirForDICOM()
+
+int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    int nDuplicates = 0;
+    for (int i = 1; i < nConvert; i++) {
+        if (dcmSort[i].img == dcmSort[i-1].img) {
+            nDuplicates ++;
+        } 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;
+}// removeDuplicates()
+
+int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    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
+            nDuplicates ++;
+        }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;
+}// 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 )
+        if(strcicmp(p_extension,ext) == 0) return true;
+    //if(strcmp(p_extension,ext) == 0) return true;
+    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;
+    nameList.numItems = 1;
+    nameList.maxItems = 1;
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
+    nameList.str[0]  = (char *)malloc(strlen(opts.indir)+1);
+    strcpy(nameList.str[0],opts.indir);
+    TDTI4D dti4D;
+    dcmList[0] = nii_readParRec(nameList.str[0], opts.isVerbose, &dti4D);
+    struct TDCMsort dcmSort[1];
+    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 > 0)
+        for (int i = 0; i < nameList.numItems; i++)
+            free(nameList.str[i]);
+    free(nameList.str);
+
+    return EXIT_SUCCESS;
+}// convert_parRec()
+
+void freeNameList(struct TSearchList nameList) {
+    if (nameList.numItems > 0) {
+        unsigned long n = nameList.numItems;
+        if (n > nameList.maxItems) n = nameList.maxItems; //assigned if (nameList->numItems < nameList->maxItems)
+        for (unsigned long i = 0; i < n; i++)
+            free(nameList.str[i]);
+    }
+    free(nameList.str);
+}
+
+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
+        return EXIT_FAILURE;
+    }
+    char indir[512];
+    strcpy(indir,opts->indir);
+    bool isFile = is_fileNotDir(opts->indir);
+    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)
+        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);
+    if (!is_dir(opts->outdir,true)) {
+		#ifdef myUseInDirIfOutDirUnavailable
+		printf("Warning: 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);
+		return EXIT_FAILURE;
+		#endif
+    }
+    /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) {
+        #ifndef myDisableTarGz
+         #ifndef myDisableZLib
+          untargz( indir, opts->outdir);
+         #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"))) ) {
+        char pname[512], rname[512];
+        strcpy(pname,indir);
+        strcpy(rname,indir);
+        changeExt (pname, "PAR");
+        changeExt (rname, "REC");
+        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
+    //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);
+        if (nameList.numItems <= nameList.maxItems)
+            break;
+        freeNameList(nameList);
+        nameList.maxItems = nameList.numItems+1;
+        //printf("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
+        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
+    // 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;
+    int nConvertTotal = 0;
+    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
+            struct TDCMsort dcmSort[1];
+            dcmSort[0].indx = i;
+            dcmSort[0].img = ((uint64_t)dcmList[i].seriesNum << 32) + dcmList[i].imageNum;
+            dcmList[i].converted2NII = 1;
+            saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D);
+            nConvertTotal++;
+        }
+    	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");
+    	}
+    }
+    //3: stack DICOMs with the same Series
+    for (int i = 0; i < nDcm; i++ ) {
+		if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) {
+			int nConvert = 0;
+			struct TWarnings warnings = setWarnings();
+			for (int j = i; j < nDcm; j++)
+				if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings) )
+					nConvert++;
+			if (nConvert < 1) nConvert = 1; //prevents compiler warning for next line: never executed since j=i always causes nConvert ++
+
+#ifdef _MSC_VER
+			TDCMsort * dcmSort = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
+#else
+			struct TDCMsort dcmSort[nConvert];
+#endif
+			nConvert = 0;
+			//warnings = setWarnings();
+			for (int j = i; j < nDcm; j++)
+				if (isSameSet(dcmList[i], dcmList[j], opts->isForceStackSameSeries, &warnings)) {
+					dcmSort[nConvert].indx = j;
+					dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32) + dcmList[j].imageNum;
+					dcmList[j].converted2NII = 1;
+					nConvert++;
+				}
+			qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers....
+			if (opts->isVerbose)
+				nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList);
+			else
+				nConvert = removeDuplicates(nConvert, dcmSort);
+			nConvertTotal += nConvert;
+			saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts, &dti4D);
+#ifdef _MSC_VER
+			free(dcmSort);
+#endif
+		}//convert all images of this series
+    }
+    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
+    }
+    freeNameList(nameList);
+    //if (nameList.numItems > 0)
+    //    for (int i = 0; i < nameList.numItems; i++)
+    //        free(nameList.str[i]);
+    //free(nameList.str);
+    return EXIT_SUCCESS;
+}// nii_loadDir()
+
+/* cleaner than findPigz - perhaps validate someday
+ void findExe(char name[512], const char * argv[]) {
+    if (is_exe(name)) return; //name exists as provided
+    char basename[1024];
+    strcpy(basename, name); //basename = source
+    //check executable folder
+    strcpy(name,argv[0]);
+    dropFilenameFromPath(name);
+    char appendChar[2] = {"a"};
+    appendChar[0] = kPathSeparator;
+    if (name[strlen(name)-1] != kPathSeparator) strcat (name,appendChar);
+    strcat(name,basename);
+    if (is_exe(name)) return; //name exists as provided
+    //check /opt
+    strcpy (name,"/opt/local/bin/" );
+    strcat (name, basename);
+    if (is_exe(name)) return; //name exists as provided
+    //check /usr
+    strcpy (name,"/usr/local/bin/" );
+    strcat (name, basename);
+    if (is_exe(name)) return;
+    strcpy(name,""); //not found!
+}*/
+
+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);
+		#else //myUseZLib
+ 		printf("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
+    #else
+    strcpy(opts->pigzname,"/usr/local/bin/pigz");
+    char pigz[1024];
+    strcpy(pigz, opts->pigzname);
+    if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/bin/pigz");
+        if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron");
+        if (!is_exe(opts->pigzname)) {
+            strcpy(opts->pigzname,argv[0]);
+            dropFilenameFromPath(opts->pigzname);//, opts.pigzname);
+            char appendChar[2] = {"a"};
+            appendChar[0] = kPathSeparator;
+            if (opts->pigzname[strlen(opts->pigzname)-1] != kPathSeparator) strcat (opts->pigzname,appendChar);
+            strcat(opts->pigzname,"pigz_mricron");
+            #if defined(_WIN64) || defined(_WIN32)
+            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);
+            	#else //myUseZLib
+                printf("Compression will be faster with %s\n",pigz);
+            	#endif
+            #endif
+                strcpy(opts->pigzname,"");
+            } //no pigz_mricron in exe's folder
+        } //no /usr/local/pigz_mricron
+       }//no /usr/bin/pigz
+    } //no /usr/local/pigz
+    #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[]) {
+    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
+    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;
+    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");
+     HKEY  hKey;
+    DWORD vSize     = 0;
+    DWORD dwDataType = 0;
+    DWORD dwValue    = 0;
+    //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle);
+    //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+    if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return;
+    }
+    vSize = sizeof(dwValue);
+    //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS)
+    if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast<BYTE *>(&dwValue), &vSize) == ERROR_SUCCESS)
+    	opts->isGz = dwValue;
+    vSize = 512;
+    char buffer[512];
+    if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS )
+ 	strcpy(opts->filename,buffer);
+ RegCloseKey(hKey);
+} //readIniFile()
+
+#else
+//for Unix we will save preferences in a hidden text file in the home directory
+#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);
+    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);
+        if ( strcmp(Setting,"isGZ") == 0 )
+            opts->isGz = atoi(Value);
+        else if ( strcmp(Setting,"isBIDS") == 0 )
+            opts->isCreateBIDS = atoi(Value);
+        else if ( strcmp(Setting,"filename") == 0 )
+            strcpy(opts->filename,Value);
+    }
+    fclose(fp);
+}// readIniFile()
+
+void saveIniFile (struct TDCMopts opts) {
+    FILE *fp = fopen(opts.optsname, "w");
+    //printf("%s\n",localfilename);
+    if (fp == NULL) return;
+    fprintf(fp, "isGZ=%d\n", opts.isGz);
+    fprintf(fp, "isBIDS=%d\n", opts.isCreateBIDS);
+    fprintf(fp, "filename=%s\n", opts.filename);
+    fclose(fp);
+} //saveIniFile()
+
+#endif
diff --git a/console/nii_dicom_batch.h b/console/nii_dicom_batch.h
new file mode 100644
index 0000000..cc8c67f
--- /dev/null
+++ b/console/nii_dicom_batch.h
@@ -0,0 +1,34 @@
+//
+//
+
+#ifndef MRIpro_nii_batch_h
+#define MRIpro_nii_batch_h
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <string.h>
+#include "nifti1.h"
+#include "nifti1.h"
+#include "nii_dicom.h"
+
+    struct TDCMopts {
+        bool isGz, isFlipY,  isCreateBIDS, isCreateText, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackSameSeries, isCrop;
+        int isVerbose, compressFlag; //support for compressed data 0=none,
+        char filename[512], outdir[512], indir[512], pigzname[512], optsname[512], indirParent[512];
+    };
+    void saveIniFile (struct TDCMopts opts);
+    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);
+    void  nii_createDummyFilename(char * niiFilename, struct TDCMopts opts);
+    //void findExe(char name[512], const char * argv[]);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/console/nii_ortho.cpp b/console/nii_ortho.cpp
new file mode 100644
index 0000000..5f00430
--- /dev/null
+++ b/console/nii_ortho.cpp
@@ -0,0 +1,378 @@
+#include "nifti1.h"
+#include "nifti1_io_core.h"
+#include "nii_ortho.h"
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+//#include <unistd.h>
+#include <stdio.h>
+#ifndef _MSC_VER
+
+	#include <unistd.h>
+
+#endif
+//#define MY_DEBUG //verbose text reporting
+
+typedef struct  {
+    int v[3];
+} vec3i;
+
+mat33 matDotMul33 (mat33 a, mat33 b)
+// in Matlab: ret = a'.*b
+{
+    mat33 ret;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            ret.m[i][j] = a.m[i][j]*b.m[j][i];
+        }
+    }
+    return ret;
+}
+
+mat33 matMul33 (mat33 a, mat33 b)
+// mult = a * b
+{
+    mat33 mult;
+    for(int i=0;i<3;i++)
+    {
+        for(int j=0;j<3;j++)
+        {
+            mult.m[j][i]=0;
+            for(int k=0;k<3;k++)
+                mult.m[j][i]+=a.m[j][k]*b.m[k][i];
+        }
+    }
+    return mult;
+}
+
+float getOrthoResidual (mat33 orig, mat33 transform)
+{
+    mat33 mat = matDotMul33(orig, transform);
+    float ret = 0;
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            ret = ret + (mat.m[i][j]);
+        }
+    }
+    return ret;
+}
+
+mat33 getBestOrient(mat44 R, vec3i flipVec)
+//flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension
+{
+    mat33 ret, newmat, orig;
+    LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2],
+               R.m[1][0],R.m[1][1],R.m[1][2],
+               R.m[2][0],R.m[2][1],R.m[2][2]);
+    float best = 0;//FLT_MAX;
+    float newval;
+    for (int rot = 0; rot < 6; rot++) { //6 rotations
+        switch (rot) {
+            case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break;
+            case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break;
+            case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break;
+            case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break;
+            case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break;
+            case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break;
+        }
+        newval = getOrthoResidual(orig, newmat);
+        if (newval > best) {
+            best = newval;
+            ret = newmat;
+        }
+    }
+    return ret;
+}
+
+bool isMat44Canonical(mat44 R)
+//returns true if diagonals >0 and all others =0
+//  no rotation is necessary - already in perfect orthogonal alignment
+{
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if ((i == j) && (R.m[i][j] <= 0) ) return false;
+            if ((i != j) && (R.m[i][j] != 0) ) return false;
+        }//j
+    }//i
+    return true;
+}
+
+vec3i setOrientVec(mat33 m)
+// Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution...
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    vec3i ret = {0, 0, 0};
+    //mat33 m = {-1,0,0, 0,1,0, 0,0,1};
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (m.m[i][j] > 0) ret.v[j] = i+1;
+            if (m.m[i][j] < 0) ret.v[j] = -(i+1);
+        }//j
+    }//i
+    return ret;
+}
+
+mat44 setMat44Vec(mat33 m33, vec3 Translations)
+//convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1
+{
+    mat44 m44;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            m44.m[i][j] = m33.m[i][j];
+        }
+    }
+    m44.m[0][3] = Translations.v[0];
+    m44.m[1][3] = Translations.v[1];
+    m44.m[2][3] = Translations.v[2];
+    m44.m[3][0] = 0;
+    m44.m[3][1] = 0;
+    m44.m[3][2] = 0;
+    m44.m[3][3] = 1;
+    return m44;
+}
+
+mat44 sFormMat(struct nifti_1_header *h) {
+    mat44 s;
+    s.m[0][0]=h->srow_x[0];
+    s.m[0][1]=h->srow_x[1];
+    s.m[0][2]=h->srow_x[2];
+    s.m[0][3]=h->srow_x[3];
+    s.m[1][0]=h->srow_y[0];
+    s.m[1][1]=h->srow_y[1];
+    s.m[1][2]=h->srow_y[2];
+    s.m[1][3]=h->srow_y[3];
+    s.m[2][0]=h->srow_z[0];
+    s.m[2][1]=h->srow_z[1];
+    s.m[2][2]=h->srow_z[2];
+    s.m[2][3]=h->srow_z[3];
+    s.m[3][0] = 0 ;
+    s.m[3][1] = 0 ;
+    s.m[3][2] = 0 ;
+    s.m[3][3] = 1 ;
+    return s;
+}
+
+void mat2sForm (struct nifti_1_header *h, mat44 s) {
+    h->srow_x[0] = s.m[0][0];
+    h->srow_x[1] = s.m[0][1];
+    h->srow_x[2] = s.m[0][2];
+    h->srow_x[3] = s.m[0][3];
+    h->srow_y[0] = s.m[1][0];
+    h->srow_y[1] = s.m[1][1];
+    h->srow_y[2] = s.m[1][2];
+    h->srow_y[3] = s.m[1][3];
+    h->srow_z[0] = s.m[2][0];
+    h->srow_z[1] = s.m[2][1];
+    h->srow_z[2] = s.m[2][2];
+    h->srow_z[3] = s.m[2][3];
+}
+
+size_t* orthoOffsetArray(int dim, int stepBytesPerVox) {
+    //return lookup table of length dim with values incremented by stepBytesPerVox
+    // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0
+    size_t *lut= (size_t *)malloc(dim*sizeof(size_t));
+    if (stepBytesPerVox > 0)
+        lut[0] = 0;
+    else
+        lut[0] = -stepBytesPerVox  *(dim-1);
+    if (dim > 1)
+        for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox;
+    return lut;
+} //orthoOffsetArray()
+
+//void  reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+void  reOrientImg( unsigned char *  img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+    //reslice data to new orientation
+    //generate look up tables
+    size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]);
+    size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]);
+    size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]);
+    //convert data
+    size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3]
+    size_t o = 0; //output address
+    uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time
+    uint8_t *outbuf = (uint8_t *) img; //source image
+    for (int vol= 0; vol < nvol; vol++) {
+        memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume
+        for (int z = 0; z < outDim.v[2]; z++)
+            for (int y = 0; y < outDim.v[1]; y++)
+                for (int x = 0; x < outDim.v[0]; x++) {
+                    memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox);
+                    o = o+ bytePerVox;
+                } //for each x
+    } //for each volume
+    //free arrays
+    free(inbuf);
+    free(xLUT);
+    free(yLUT);
+    free(zLUT);
+} //reOrientImg
+
+unsigned char *  reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM)
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    size_t nvox = h->dim[1] * h->dim[2] * h->dim[3];
+    if (nvox < 1) return img;
+    vec3i outDim= {0,0,0};
+    vec3i outInc= {0,0,0};
+    for (int i = 0; i < 3; i++) { //set dimension, pixdim and
+        outDim.v[i] =  h->dim[abs(orientVec.v[i])];
+        if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1;
+        if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1];
+        if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2];
+        if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip
+    } //for each dimension
+    int nvol = 1; //convert all non-spatial volumes from source to destination
+    for (int vol = 4; vol < 8; vol++) {
+        if (h->dim[vol] > 1)
+            nvol = nvol * h->dim[vol];
+    }
+    reOrientImg(img, outDim, outInc, h->bitpix / 8,  nvol);
+    //now change the header....
+    vec3 outPix= {h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]};
+    for (int i = 0; i < 3; i++) {
+        h->dim[i+1] = outDim.v[i];
+        h->pixdim[i+1] = outPix.v[i];
+    }
+    mat44 s = sFormMat(h);
+    mat33 mat; //computer transform
+    LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2],
+                         s.m[1][0],s.m[1][1],s.m[1][2],
+                         s.m[2][0],s.m[2][1],s.m[2][2]);
+    mat = matMul33(  mat, orient);
+    s = setMat44Vec(mat, minMM); //add offset
+    mat2sForm(h,s);
+    h->qform_code = h->sform_code; //apply to the quaternion as well
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    return img;
+} //reOrient()
+
+float getDistance (vec3 v, vec3 min)
+//scalar distance between two 3D points - Pythagorean theorem
+{
+    return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2)  );
+}
+
+vec3 xyz2mm (mat44 R, vec3 v)
+{
+    vec3 ret;
+    for (int i = 0; i < 3; i++) {
+        ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] );
+    }
+    return ret;
+}
+
+vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec)
+//orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift
+// a simple solution is to first compute the most left, posterior, inferior voxel in the source image
+// this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix...
+{
+    int i,j, minIndex;
+    vec3i flipVecs[8];
+    vec3 corner[8], min;
+    mat44 s = sFormMat(h);
+    for (int i = 0; i < 8; i++) {
+        if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1;
+        if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1;
+        if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1;
+        corner[i] = setVec3(0,0,0); //assume no reflections
+        if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X
+        if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y
+        if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z
+        corner[i] = xyz2mm(s,corner[i]);
+    }
+    //find extreme edge from ALL corners....
+    min = corner[0];
+    for (i = 1; i < 8; i++) {
+        for (j = 0; j < 3; j++) {
+            if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j];
+        }
+    }
+    float dx; //observed distance from corner
+    float min_dx = getDistance (corner[0], min);
+    minIndex = 0; //index of corner closest to min
+    //see if any corner is closer to absmin than the first one...
+    for (i = 1; i < 8; i++) {
+        dx = getDistance (corner[i], min);
+        if (dx < min_dx) {
+            min_dx = dx;
+            minIndex = i;
+        }
+    }
+    min = corner[minIndex]; //this is the single corner closest to min from all
+    *flipVec= flipVecs[minIndex];
+    return min;
+}
+
+#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,
+           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]);
+}
+#endif
+
+unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
+    if ((h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img;
+    if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided
+        mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d,
+                                         h->qoffset_x, h->qoffset_y, h->qoffset_z,
+                                         h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]);
+        mat2sForm(h,q); //convert q-form to s-form
+        h->sform_code = h->qform_code;
+    }
+    if (h->sform_code == NIFTI_XFORM_UNKNOWN) {
+         #ifdef MY_DEBUG
+         printf("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");
+        #endif
+        return img;
+    }
+    vec3i  flipV;
+    vec3 minMM = minCornerFlip(h, &flipV);
+    mat33 orient = getBestOrient(s, flipV);
+    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");
+        #endif
+        return img;
+    }
+    bool is24 = false;
+    if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices
+        return img;
+        is24 = true;
+        h->bitpix = 8;
+        h->dim[3] = h->dim[3] * 3;
+    }
+    img = reOrient(img, h,orientVec, orient, minMM);
+    if (is24 ) {
+        h->bitpix = 24;
+        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]);
+    reportMat44o((char*)"input",s);
+    s = sFormMat(h);
+    reportMat44o((char*)"output",s);
+    #endif
+    return img;
+}
+
+
+
diff --git a/console/nii_ortho.h b/console/nii_ortho.h
new file mode 100644
index 0000000..1fc9ac7
--- /dev/null
+++ b/console/nii_ortho.h
@@ -0,0 +1,16 @@
+#ifndef _NIFTI_ORTHO_CORE_
+#define _NIFTI_ORTHO_CORE_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+    
+#include "nifti1.h"
+    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);
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
diff --git a/console/nii_ostu_ml.cpp b/console/nii_ostu_ml.cpp
new file mode 100644
index 0000000..799cc7a
--- /dev/null
+++ b/console/nii_ostu_ml.cpp
@@ -0,0 +1,628 @@
+
+#include "nii_ostu_ml.h"
+#include <stdint.h>
+#include <math.h>
+#include <stdlib.h> // pulls in declaration of malloc, free
+#include <string.h> // pulls in declaration for strlen.
+#include <stdio.h>
+
+//Otsu's Method [Multilevel]
+//Otsu N (1979) A threshold selection method from gray-level histograms. IEEE Trans. Sys., Man., Cyber. 9: 62-66.
+//Lookup Tables as suggested by Liao, Chen and Chung (2001) A fast algorithm for multilevel thresholding
+//note my "otsu" method is slightly faster and much simpler than otsu_ml if you only want bi-level output
+
+
+//typedef double HistoRAd[256];
+typedef double Histo2D[256][256];
+
+void otsuLUT(int *H, Histo2D result) //H is histogram 0..255
+{
+    for (int u = 0; u < 256; u++)
+        for (int v = 0; v < 256; v++)
+            result[u][v] = 0;
+    double sum,prob;
+    sum = 0;
+    for (int v = 0; v < 256; v++)
+        sum = sum + H[v];
+    if (sum <= 0) return;
+    Histo2D P,S;
+    P[0][0] = H[0];
+    S[0][0] = H[0];
+    for (int v = 1; v < 256; v++) {
+        prob = H[v]/sum;
+        P[0][v] = P[0][v-1]+prob;
+        S[0][v] = S[0][v-1]+(v+1)*prob;
+    }
+    for (int u = 1; u < 256; u++) {
+        for (int v = u; v < 256; v++) {
+            P[u][v] = P[0][v]-P[0][u-1];
+            S[u][v] = S[0][v]-S[0][u-1];
+        }
+    }
+
+    //result is eq 29 from Liao
+    for (int u = 0; u < 256; u++) {
+        for (int v = u; v < 256; v++) {
+            if (P[u][v] == 0)  //avoid divide by zero errors...
+                result[u][v] = 0;
+            else
+                result[u][v] = pow(S[u][v],2) /P[u][v];
+        }
+    }
+}
+
+
+void otsuCostFunc4(int *lHisto, int *low, int *middle, int *high)
+//call with lHisto &lo &hi
+{
+    double v,max;
+    Histo2D h2d;
+    otsuLUT(lHisto, h2d);
+    int lo = 12;
+    int mid = 128;
+    int hi = 192;
+    max = h2d[0][lo]+h2d[lo+1][mid]+h2d[mid+1][hi]+h2d[hi+1][255];
+    //exhaustively search
+    for (int l = 0; l < (255-2); l++) {
+        for (int m = l+1; m < (255-1); m++ ) {
+            for (int h = m+1; h < (255); h++) {
+                v = h2d[0][l]+h2d[l+1][m]+h2d[m+1][h]+h2d[h+1][255];
+                if (v > max) {
+                    lo = l;
+                    mid = m;
+                    hi = h;
+                    max = v;
+                }//new max
+            }//for h -> hi
+        }//for mid
+    }//for l -> low
+    *low = lo;
+    *middle = mid;
+    *high = hi;
+}//quad OtsuCostFunc4
+
+int otsuCostFunc2(int *lHisto)
+{
+    double v,max;
+    Histo2D h2d;
+    otsuLUT(lHisto, h2d);
+    //default solution - 128 is middle intensity of 0..255
+    int  n = 128;
+    max = h2d[0][n]+h2d[n+1][255];
+    int result = n;
+    //exhaustively search
+    for (n = 0; n < 255; n++) {
+        v = h2d[0][n]+h2d[n+1][255];
+        if (v > max) {
+            result = n;
+            max = v;
+        }//max
+    } //for n
+    return result;
+} //bilevel otsuCostFunc2
+
+void otsuCostFunc3(int *lHisto, int *low, int *high)
+//call with lHisto &lo &hi
+{
+    double v,max;
+    Histo2D h2d;
+    otsuLUT(lHisto, h2d);
+    int lo = 85;
+    int hi = 170;
+    //default solution
+    max = h2d[0][lo]+h2d[lo+1][hi]+h2d[hi+1][255];
+    //exhaustively search
+    for (int l = 0; l < (255-1); l++) {
+        for (int h = l+1; h < (255); h++ ) {
+            v = h2d[0][l]+h2d[l+1][h]+h2d[h+1][255];
+            if (v > max) {
+                lo = l;
+                hi = h;
+                max = v;
+            } //new max
+        }//for h -> hi
+    }//for l -> low
+    *low = lo;
+    *high = hi;
+}; //trilevel OtsuCostFunc3
+
+int findOtsu2 (uint8_t *img8bit, int nVox)
+{
+    int lHisto[256];
+    if (nVox < 1) return 128;
+    //create histogram
+    for (int n = 0; n < 256; n++)
+        lHisto[n] = 0;
+    for (int n = 0; n < nVox; n++)
+        lHisto[img8bit[n]]++;
+    //now find minimum intraclass variance....
+    return otsuCostFunc2(lHisto);
+}
+
+void findOtsu3 (uint8_t *img8bit, int nVox, int* lo, int* hi)
+{
+    int lHisto[256];
+    *lo = 85;
+    *hi = 170;
+    if (nVox < 1) return;
+    //create histogram
+    for (int n = 0; n < 256; n++)
+        lHisto[n] = 0;
+    for (int n = 0; n < nVox; n++)
+        lHisto[img8bit[n]]++;
+    otsuCostFunc3(lHisto,lo,hi);
+}
+
+void findOtsu4 (uint8_t *img8bit, int nVox, int* lo, int* mid, int* hi)
+{
+    int lHisto[256];
+    *lo = 64;
+    *mid = 128;
+    *hi = 192;
+    if (nVox < 1) return;
+    //create histogram
+    for (int n = 0; n < 256; n++)
+        lHisto[n] = 0;
+    for (int n = 0; n < nVox; n++)
+        lHisto[img8bit[n]]++;
+    otsuCostFunc4(lHisto,lo,mid, hi);
+}
+
+void applyOtsu2 (uint8_t *img8bit, int nVox)
+{
+    if (nVox < 1) return;
+    int result = findOtsu2(img8bit,nVox);
+    for (int n = 0; n < nVox; n++) {
+        if (img8bit[n] > result)
+            img8bit[n] = 255;
+        else
+            img8bit[n] = 0;
+    }
+    return;
+}//applyOtsu2
+
+void applyOtsu3 (uint8_t *img8bit, int nVox)
+{
+    if (nVox < 1) return;
+    int lo, hi;
+    findOtsu3(img8bit,nVox, &lo, &hi);
+    for (int n = 0; n < nVox; n++) {
+        if (img8bit[n] <= lo)
+            img8bit[n] = 0;
+        else if (img8bit[n] >= hi)
+            img8bit[n] = 255;
+        else
+            img8bit[n] = 128;
+    }
+    return;
+}//applyOtsu3
+
+void applyOtsu4 (uint8_t *img8bit, int nVox)
+{
+    if (nVox < 1) return;
+    int lo, mid, hi;
+    findOtsu4(img8bit,nVox, &lo, &mid, &hi);
+    for (int n = 0; n < nVox; n++) {
+        if (img8bit[n] <= lo)
+            img8bit[n] = 0;
+        else if (img8bit[n] <= mid)
+            img8bit[n] = 85;
+        else if (img8bit[n] <= hi)
+            img8bit[n] = 170;
+        else
+            img8bit[n] = 255;
+    }
+    return;
+}//applyOtsu4
+
+void applyOtsuBinary (uint8_t *img8bit, int nVox, int levels)
+//1=1/4, 2=1/3, 3=1/2, 4=2/3, 5=3/4
+{
+    if (nVox < 1) return;
+    //for (int n = 0; n < nVox; n++)
+    //    if (img8bit[n] < 10)
+    //        img8bit[n] = 0;
+    if ((levels <= 1) || (levels >= 5)) {
+        applyOtsu4(img8bit,nVox);
+    } else if ((levels = 2) || (levels = 4))
+        applyOtsu3(img8bit,nVox);
+    else //level = 3
+        applyOtsu2(img8bit,nVox);
+    int H[256];
+    if (levels <= 3) { //make dark: all except 255 equal 0
+        for (int n = 0; n < 256; n++)
+            H[n] = 0;
+        H[255] = 255;
+    } else { //make bright: all except 0 equal 255
+        for (int n = 0; n < 256; n++)
+            H[n] = 255;
+        H[0] = 0;
+    }
+    for (int n = 0; n < nVox; n++)
+        img8bit[n] = H[img8bit[n]];
+}
+
+void countClusterSize (uint8_t *lImg, int64_t *lClusterBuff, int lXi, int lYi, int lZi,  uint8_t lClusterValue)
+{
+    if ((lXi < 5) || (lYi < 5) || (lZi < 3)) return;
+    int const kUndeterminedSize = -1;
+    int const kCurrentSize = -2;
+    //int lXY = lXi*lYi; //offset one slice
+    int lXYZ =lXi*lYi*lZi;
+    int64_t *lQra = (int64_t *) malloc(lXYZ * sizeof(int64_t));
+    //initialize clusterbuffer: voxels either non part of clusters (cluster size 0), or part of cluster of undetermined size
+    for (int v = 0; v < lXYZ; v++) {
+        if (lImg[v] == lClusterValue)
+            lClusterBuff[v] = kUndeterminedSize;  //target voxel - will be part of a cluster of size 1..XYZ
+        else
+            lClusterBuff[v] = 0;//not a target, not part of a cluser, size = 0
+    }//for lInc
+    int const nSearch = 6;
+    int64_t *searchArray = (int64_t *) malloc(nSearch * sizeof(int64_t));
+    searchArray[0] = -1; //left
+    searchArray[1] = 1; //right
+    searchArray[2] = -lXi; //posterior
+    searchArray[3] = lXi; //anterior
+    searchArray[4] = -lXi*lYi; //inferior
+    searchArray[5] = lXi*lYi; //superior
+    for (int v = 0; v < lXYZ; v++) {
+        if (lClusterBuff[v] == kUndeterminedSize) {
+            int lQCurr = 0;
+            int lQMax = 0;
+            lQra[lQCurr] = v;
+            lClusterBuff[v] = kCurrentSize;
+            do {
+                for (int s = 0; s < nSearch; s++) {//search each neighbor to see if it is part of this cluster
+                    int64_t vx = lQra[lQCurr] + searchArray[s];
+                    if ((vx >= 0) && (vx < lXYZ) && (lClusterBuff[vx] == kUndeterminedSize) ) {
+                        lClusterBuff[vx] = kCurrentSize;
+                        lQMax++;
+                        lQra[lQMax] = vx;
+                    }
+                }
+                lQCurr++;
+            } while (lQMax >= lQCurr);
+            //set all voxels in this cluster to reflect their size
+            for (int vx = 0; vx < lQCurr; vx++)
+                lClusterBuff[ lQra[vx]  ] = lQCurr;
+        }//if v part of new cluser
+    } //for each voxel
+    free(searchArray);
+    //    selectClusters (lClusterBuff, -1, lXi, lXYZ, lQSz,  lTopSlice, lXY, lQra);
+    free(lQra);
+} //CountClusterSize
+
+
+ //fillBubbles works but is typically not required - increases processing time ~20%
+void fillBubbles (uint8_t *lImg, int lXi, int lYi, int lZi)
+//output voxels only zero if they are connected to edge voxels by zeros....
+{
+    if ((lXi < 5) or (lYi < 5) or (lZi < 1)) return;
+    int lXYZ = lXi *lYi * lZi;
+    int const kUntouchedZero = 10;
+    int const nSearch = 6;
+    int64_t *searchArray = (int64_t *) malloc(nSearch * sizeof(int64_t));
+    int64_t *lQra = (int64_t *) malloc(lXYZ * sizeof(int64_t));
+    searchArray[0] = -1; //left
+    searchArray[1] = 1; //right
+    searchArray[2] = -lXi; //posterior
+    searchArray[3] = lXi; //anterior
+    searchArray[4] = -lXi*lYi; //inferior
+    searchArray[5] = lXi*lYi; //superior
+    for (int v = 0; v < lXYZ; v++) {//make binary
+        if (lImg[v] != 0)
+            lImg[v] = 255;
+        else
+            lImg[v] = kUntouchedZero; //zero - but not yet sure if connected to edge...
+    } //for v each voxel
+    for (int z = 1; z <= lZi; z++) {
+        int zpos = (z-1) * lXi * lYi;
+        for (int y = 1; y <= lYi; y++) {
+            int ypos = (y-1) * lXi;
+            for (int x = 1; x <= lXi; x++) {
+                if ((x==1) || (y==1) || (z==1) || (x==lXi) || (y==lYi) || (z==lZi) ) { //voxel on edge
+                    int v = zpos + ypos + x -1;
+                    if (lImg[v] == kUntouchedZero) {
+                        int lQCurr = 0;
+                        int lQMax = 0;
+                        lQra[lQCurr] = v;
+                        lImg[v] = 0;
+                        do {
+                            for (int s = 0; s < nSearch; s++) {//search each neighbor to see if it is part of this cluster
+                                int64_t vx = lQra[lQCurr] + searchArray[s];
+                                if ((vx >= 0) && (vx < lXYZ) && (lImg[vx] == kUntouchedZero) ) {
+                                    lImg[vx] = 0;
+                                    lQMax++;
+                                    lQra[lQMax] = vx;
+                                }
+                            }
+                            lQCurr++;
+                        } while (lQMax >= lQCurr);
+                    }//untouched zero
+                } //on edge
+            } //for x
+        } //for y
+    } //for z
+    free(searchArray);
+    free(lQra);
+    for (int v = 0; v < lXYZ; v++) // untouched voxels not connected to any edge
+        if (lImg[v] == kUntouchedZero)
+            lImg[v] = 255;
+} //fillBubbles - fill holes inside object
+
+
+void preserveLargestCluster (uint8_t *lImg, int lXi, int lYi, int lZi, int lClusterValue, uint8_t ValueForSmallClusters)
+{
+    if ((lXi < 5) or (lYi < 5) or (lZi < 1)) return;
+    int lXYZ,lX;
+    int64_t lC;
+    lXYZ =lXi*lYi*lZi;
+    //ensure at least some voxels exist with clusterValue
+    lC = 0;
+    for (lX =0; lX< lXYZ; lX++)
+        if (lImg[lX] == lClusterValue)
+            lC++;
+    if (lC < 2)
+        return;//e.g. if lC = 1 then only a single voxel, which is in fact largest cluster
+    int64_t *lTemp = (int64_t *) malloc(lXYZ * sizeof(int64_t));
+    for (lX = 0; lX < lXYZ; lX++) lTemp[lX] = 0; //the only purpose of this loop is to hide a compiler warning - countClusterSize will fill this array...
+    countClusterSize(lImg,lTemp,lXi,lYi,lZi,lClusterValue);
+    lC = 0;
+    for (lX = 0; lX < lXYZ; lX++)
+        if (lTemp[lX] > lC)
+            lC = lTemp[lX]; //volume of largest cluster
+    if (ValueForSmallClusters == 0) {
+        for (lX = 0; lX < lXYZ; lX++)
+            if ((lTemp[lX] >= 0) && (lTemp[lX] < lC)) //cluster, but not biggest one...
+                lImg[lX] = ValueForSmallClusters;
+    } else {
+        for (lX = 0; lX < lXYZ; lX++)
+            if ((lTemp[lX] > 0) && (lTemp[lX] < lC)) //cluster, but not biggest one...
+                lImg[lX] = ValueForSmallClusters;
+    }
+    free(lTemp);
+}
+
+void dilate (uint8_t *lImg, int lXi, int lYi, int lZi, int lCycles, int lChange)
+//Dilates Diamonds - neighbor coefficient = 0
+//Dilate if Change=1 then all voxels where intensity <> 1 but where any neighbors = 1 will become 1
+//Erode  if Change=0 then all voxels where intensity <>0 but where any neighbors = 0 will become 0
+//step is repeated  for lCycles
+{
+    if ((lXi < 5) || (lYi < 5) || (lZi < 1))
+        return;
+    int lX,lY,lZ, lXY,lXYZ,lPos,lOffset;
+    lXY = lXi*lYi; //offset one slice
+    lXYZ =lXY*lZi;
+    uint8_t *lTemp = (uint8_t *) malloc(lXYZ);
+    for (int lC = 0; lC < lCycles; lC++) {
+        memcpy (lTemp, lImg,  lXYZ);
+        for (lZ = 0; lZ < lZi; lZ++) {
+            for (lY = 0; lY < lYi; lY++) {
+                lOffset = (lY*lXi) + (lZ * lXY);
+                for (lX = 0; lX < lXi; lX++) {
+                    lPos = lOffset + lX;
+                    if (lTemp[lPos] != lChange) {
+                        if ((lX>0) && (lTemp[lPos-1] == lChange))
+                            lImg[lPos] = lChange;
+                        else if ((lX<(lXi-1)) && (lTemp[lPos+1] == lChange))
+                            lImg[lPos] = lChange;
+                        else if ((lY>0) && (lTemp[lPos-lXi] == lChange))
+                            lImg[lPos] = lChange;
+                        else if ((lY<(lYi-1)) && (lTemp[lPos+lXi] == lChange))
+                            lImg[lPos] = lChange;
+                        else if ((lZ>0) && (lTemp[lPos-lXY] == lChange))
+                            lImg[lPos] = lChange;
+                        else if ((lZ<(lZi-1)) && (lTemp[lPos+lXY] == lChange))
+                            lImg[lPos] = lChange;
+                    } //if not lChange
+                } //for X
+            } //for Y
+        } //for Z
+    } //for each cycle
+    free(lTemp);
+} //dilate
+
+void dilateSphere (uint8_t *lImg, int lXi, int lYi, int lZi, float lVoxDistance, int lChange)
+//INPUT: Img is array of bytes 0..(X*Y*Z-1) that represents 3D volume, lXi,lYi,lZi are number of voxels in each dimension
+//             lVoxDistance is search radius (in voxels)
+//            lChange is the intensity to be changed - if background color: erosion, if foreground color: dilation
+//OUTPUT: Eroded/Dilated Img
+{
+    if (lVoxDistance < 1) return;
+    if (lVoxDistance == 1) { //much faster to use classic neighbor dilation
+        dilate(lImg,lXi,lYi,lZi,1,lChange);
+        return;
+    }
+    if ((lXi < 3) || (lYi < 3) || (lZi < 3))
+        return;
+    float lDx;
+    int lXY = lXi*lYi; //voxels per slice
+    int lXYZ = lXi*lYi*lZi; //voxels per volume
+    //next: make 1D array of all voxels within search sphere: store offset from center
+    int lDxI = trunc(lVoxDistance); //no voxel will be searched further than DxI from center
+    int64_t *lSearch = (int64_t *) malloc( ((lDxI *2)+1)*((lDxI *2)+1)*((lDxI *2)+1) *sizeof(int64_t) );
+    int lVoxOK = 0;
+    for (int lZ = -lDxI; lZ <= lDxI; lZ++) {
+        for (int lY = -lDxI; lY <= lDxI; lY++) {
+            for (int lX = -lDxI; lX <= lDxI; lX++) {
+                lDx = sqrt( (lX*lX)+ (lY*lY)+ (lZ*lZ)  );
+                if ((lDx < lVoxDistance) && (lDx > 0)) {
+                    lSearch[lVoxOK] = lX + (lY*lXi) + (lZ * lXY); //offset to center
+                    lVoxOK++;
+                } //in range, not center
+            } //lX
+        } //lY
+    }//lZ
+    uint8_t *lTemp = (uint8_t *) malloc(lXYZ);
+    memcpy (lTemp, lImg,  lXYZ);
+    for (int lVox = 0; lVox < lXYZ; lVox++)
+        if (lTemp[lVox] != lChange) { //voxel not a survivor
+            for (int s = 0; s < lVoxOK; s++) { //make voxel a survivor if neighbor is a survivor
+                int64_t t = lVox +lSearch[s];
+                if ((t >= 0) && (t < lXYZ) && (lTemp[t] == lChange)) {
+                    lImg[lVox] = lChange;
+                    break;
+                } //if neighbor
+            } //for s: search for neighboring survivrs
+        } //if lTemp[lVox] not a survivor
+    free(lTemp); //free temporary buffer
+    free(lSearch); //free 1D search space
+}//proc DilateSphere
+
+void smoothFWHM2Vox (uint8_t *lImg, int lXi, int lYi, int lZi)
+{
+    if ((lXi < 5) || (lYi < 5)  || (lZi < 1)) return;
+    int k0=240;//weight of center voxel
+    int k1=120;//weight of nearest neighbors
+    int k2=15;//weight of subsequent neighbors
+    int kTot=k0+k1+k1+k2+k2; //weight of center plus all neighbors within 2 voxels
+    int kWid = 2; //we will look +/- 2 voxels from center
+    int lyPos,lPos,lWSum,lXi2,lXY,lXY2;
+    lXY = lXi*lYi; //offset one slice
+    int lXYZ = lXY * lZi;
+    lXY2 = lXY * 2; //offset two slices
+    lXi2 = lXi*2;//offset to voxel two lines above or below
+    uint8_t *lTemp = (uint8_t *) malloc(lXYZ);
+    memcpy (lTemp, lImg,  lXYZ);
+    //smooth horizontally
+    for (int lZ = 0; lZ < lZi; lZ++) {
+        for (int lY = 0; lY < lYi; lY++) {
+            lyPos = (lY*lXi) + (lZ*lXY) ;
+            for (int lX = kWid; lX < (lXi-kWid); lX++)  {
+                lPos = lyPos + lX;
+                lWSum = lImg[lPos-2]*k2 +lImg[lPos-1]*k1 +lImg[lPos]*k0 +lImg[lPos+1]*k1 +lImg[lPos+2]*k2;
+                lTemp[lPos] = lWSum / kTot;
+            } //for lX
+        } //for lY
+    } //for lZi
+    //smooth vertically
+    memcpy (lImg,lTemp,  lXYZ);
+    for (int lZ = 0; lZ < lZi; lZ++) {
+        for (int lX = 0; lX < lXi; lX++) {
+            for (int lY = kWid; lY < (lYi-kWid); lY++) {
+                lPos = ((lY*lXi) + lX + (lZ*lXY) ) ;
+                lWSum = lTemp[lPos-lXi2]*k2+lTemp[lPos-lXi]*k1+lTemp[lPos]*k0+lTemp[lPos+lXi]*k1+lTemp[lPos+lXi2]*k2;
+                lImg[lPos] = lWSum / kTot;
+            }//for Y
+        } //for X
+    } //for Z
+    //if 3rd dimension....
+    if (lZi >= 5) {
+        //smooth across slices
+        memcpy (lTemp, lImg, lXYZ);
+        for (int lZ = kWid; lZ < (lZi-kWid); lZ++) {
+            for (int lY = 0; lY < lYi; lY++) {
+                lyPos = (lY*lXi) + (lZ*lXY) ;
+                for (int lX = 0; lX < lXi; lX++)  {
+                    lPos = lyPos + lX;
+                    lWSum = lImg[lPos-lXY2]*k2+lImg[lPos-lXY]*k1+lImg[lPos]*k0+lImg[lPos+lXY]*k1+lImg[lPos+lXY2]*k2;
+                    lTemp[lPos] = lWSum / kTot;
+                }//for lX
+            }//for lY
+        } //for lZi
+        memcpy ( lImg, lTemp, lXYZ);
+    }//if Z: at least 5 slices...
+    free(lTemp);
+} //smoothFWHM2Vox
+
+void maskBackground  (uint8_t *img8bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject)
+{
+    if ((lXi < 3) or (lYi < 3) or (lZi < 1)) return;
+    int lXYZ = lXi * lYi * lZi;
+    //countSurvivors(img8bit, lXYZ);
+    smoothFWHM2Vox(img8bit, lXi,lYi,lZi);
+    applyOtsuBinary (img8bit, lXYZ,lOtsuLevels);
+    //clip edges to prevent wrap
+    int lV=0;
+    for (int lZ = 1; lZ <= lZi; lZ++)
+        for (int lY = 1; lY <= lYi; lY++)
+            for (int lX = 1; lX <= lXi; lX++) {
+                if ((lX==1) || (lX==lXi) || (lY==1) || (lY==lYi) || (lZ==1) || (lZ==lZi))
+                    img8bit[lV] = 0;
+                lV++;
+            } //for lX
+    if (lOneContiguousObject) {
+        preserveLargestCluster (img8bit, lXi,lYi,lZi,255,0 ); //only preserve largest single object
+        if (lDilateVox > 0)
+            dilateSphere (img8bit, lXi,lYi,lZi,lDilateVox,255 );
+    } else {
+        if (lDilateVox > 0)
+            dilateSphere (img8bit, lXi,lYi,lZi,lDilateVox,255 );
+        preserveLargestCluster (img8bit, lXi,lYi,lZi,0,255 ); //only erase outside air
+    }
+    fillBubbles(img8bit, lXi,lYi,lZi); //<- optional
+    //my sense is filling air bubbles will help SPM's mixture of gaussians detect air. Without this air will have an artificially low variance
+}
+
+int compare_short (const void *a, const void *b)
+{
+  const short *da = (const short *) a;
+  const short *db = (const short *) b;
+  return (*da > *db) - (*da < *db);
+}
+
+
+void maskBackground16  (short *img16bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject) {
+    if ((lXi < 3) or (lYi < 3) or (lZi < 1)) return;
+    int lXYZ = lXi * lYi * lZi;
+    if (lXYZ < 100) return;
+    //find image intensity range
+    short * imgSort = (short *) malloc(sizeof(short)*lXYZ);
+	memcpy(&imgSort[0], &img16bit[0], lXYZ * sizeof(short)); //memcpy( dest, src, bytes)
+    qsort (imgSort, lXYZ, sizeof (short), compare_short);
+    int pctLo = imgSort[(int)((float)lXYZ * 0.02)];
+    int pctHi = imgSort[(int)((float)lXYZ * 0.98)];
+	free(imgSort);
+	if (pctLo >= pctHi) return; //no variability
+	//re-scale data 0..255
+	float scale = 255.0/(float)(pctHi - pctLo);
+	uint8_t *img8bit = (uint8_t *) malloc(sizeof(uint8_t)*lXYZ);
+	for (int i = 0; i < lXYZ; i++) {
+		if (img16bit[i] >= pctHi)
+			img8bit[i] = 255;
+		else if (img16bit[i] <= pctLo)
+			img8bit[i] = 0;
+		else
+			img8bit[i] = (int)((img16bit[i] - pctLo) * scale);
+	}
+	//mask 8-bit image
+	maskBackground  (img8bit, lXi, lYi, lZi, lOtsuLevels, lDilateVox, lOneContiguousObject);
+	for (int i = 0; i < lXYZ; i++)
+		if (img8bit[i] == 0)
+			img16bit[i] = 0;
+}
+
+void maskBackgroundU16  (unsigned short *img16bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject) {
+    if ((lXi < 3) or (lYi < 3) or (lZi < 1)) return;
+    int lXYZ = lXi * lYi * lZi;
+    if (lXYZ < 100) return;
+    //find image intensity range
+    unsigned short * imgSort = (unsigned short *) malloc(sizeof(unsigned short)*lXYZ);
+	memcpy(&imgSort[0], &img16bit[0], lXYZ * sizeof(short)); //memcpy( dest, src, bytes)
+    qsort (imgSort, lXYZ, sizeof (short), compare_short);
+    int pctLo = imgSort[(int)((float)lXYZ * 0.02)];
+    int pctHi = imgSort[(int)((float)lXYZ * 0.98)];
+	free(imgSort);
+	if (pctLo >= pctHi) return; //no variability
+	//re-scale data 0..255
+	float scale = 255.0/(float)(pctHi - pctLo);
+	uint8_t *img8bit = (uint8_t *) malloc(sizeof(uint8_t)*lXYZ);
+	for (int i = 0; i < lXYZ; i++) {
+		if (img16bit[i] >= pctHi)
+			img8bit[i] = 255;
+		else if (img16bit[i] <= pctLo)
+			img8bit[i] = 0;
+		else
+			img8bit[i] = (int)((img16bit[i] - pctLo) * scale);
+	}
+	//mask 8-bit image
+	maskBackground  (img8bit, lXi, lYi, lZi, lOtsuLevels, lDilateVox, lOneContiguousObject);
+	for (int i = 0; i < lXYZ; i++)
+		if (img8bit[i] == 0)
+			img16bit[i] = 0;
+}
+
+
+
+
diff --git a/console/nii_ostu_ml.h b/console/nii_ostu_ml.h
new file mode 100644
index 0000000..589f1d4
--- /dev/null
+++ b/console/nii_ostu_ml.h
@@ -0,0 +1,19 @@
+#include <stdint.h>
+
+
+#ifndef nii_otsu_h
+#define nii_otsu_h
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+    void maskBackground  ( 	uint8_t *img8bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject);
+	void maskBackground16  (short *img16bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject);
+	void maskBackgroundU16  (unsigned short *img16bit, int lXi, int lYi, int lZi, int lOtsuLevels, float lDilateVox, bool lOneContiguousObject);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/console/tinydir.h b/console/tinydir.h
new file mode 100644
index 0000000..cc65b7f
--- /dev/null
+++ b/console/tinydir.h
@@ -0,0 +1,441 @@
+/*
+Copyright (c) 2013-2014, Cong Xu
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#pragma warning (disable : 4996)
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+#endif
+
+
+/* types */
+
+#define _TINYDIR_PATH_MAX 4096
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+#define _TINYDIR_PATH_EXTRA 2
+#else
+#define _TINYDIR_PATH_EXTRA 0
+#endif
+#define _TINYDIR_FILENAME_MAX 256
+
+#ifdef _MSC_VER
+#define _TINYDIR_FUNC static __inline
+#else
+#define _TINYDIR_FUNC static __inline__
+#endif
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	char name[_TINYDIR_FILENAME_MAX];
+	int is_dir;
+	int is_reg;
+
+#ifdef _MSC_VER
+#else
+	struct stat _s;
+#endif
+} tinydir_file;
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	int has_next;
+	size_t n_files;
+
+	tinydir_file *_files;
+#ifdef _MSC_VER
+	HANDLE _h;
+	WIN32_FIND_DATA _f;
+#else
+	DIR *_d;
+	struct dirent *_e;
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path)
+{
+	if (dir == NULL || path == NULL || strlen(path) == 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	/* initialise dir */
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	dir->_d = NULL;
+#endif
+	tinydir_close(dir);
+
+	strcpy(dir->path, path);
+#ifdef _MSC_VER
+	strcat(dir->path, "\\*");
+	dir->_h = FindFirstFile(dir->path, &dir->_f);
+	dir->path[strlen(dir->path) - 2] = '\0';
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	dir->_d = opendir(path);
+	if (dir->_d == NULL)
+#endif
+	{
+		errno = ENOENT;
+		goto bail;
+	}
+	/* read first file */
+	dir->has_next = 1;
+#ifndef _MSC_VER
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+	{
+		dir->has_next = 0;
+	}
+#endif
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path)
+{
+	/* Count the number of files first, to pre-allocate the files array */
+	size_t n_files = 0;
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+	while (dir->has_next)
+	{
+		n_files++;
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+	}
+	tinydir_close(dir);
+
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	dir->n_files = 0;
+	dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files);
+	if (dir->_files == NULL)
+	{
+		errno = ENOMEM;
+		goto bail;
+	}
+	while (dir->has_next)
+	{
+		tinydir_file *p_file;
+		dir->n_files++;
+
+		p_file = &dir->_files[dir->n_files - 1];
+		if (tinydir_readfile(dir, p_file) == -1)
+		{
+			goto bail;
+		}
+
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+
+		/* Just in case the number of files has changed between the first and
+		second reads, terminate without writing into unallocated memory */
+		if (dir->n_files == n_files)
+		{
+			break;
+		}
+	}
+
+	qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		return;
+	}
+
+	memset(dir->path, 0, sizeof(dir->path));
+	dir->has_next = 0;
+	dir->n_files = 0;
+	if (dir->_files != NULL)
+	{
+		free(dir->_files);
+	}
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	if (dir->_h != INVALID_HANDLE_VALUE)
+	{
+		FindClose(dir->_h);
+	}
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	if (dir->_d)
+	{
+		closedir(dir->_d);
+	}
+	dir->_d = NULL;
+	dir->_e = NULL;
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (!dir->has_next)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+#ifdef _MSC_VER
+	if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+#endif
+	{
+		dir->has_next = 0;
+#ifdef _MSC_VER
+		if (GetLastError() != ERROR_SUCCESS &&
+			GetLastError() != ERROR_NO_MORE_FILES)
+		{
+			tinydir_close(dir);
+			errno = EIO;
+			return -1;
+		}
+#endif
+	}
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+#ifdef _MSC_VER
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	if (dir->_e == NULL)
+#endif
+	{
+		errno = ENOENT;
+		return -1;
+	}
+	if (strlen(dir->path) +
+		strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) + 1 + _TINYDIR_PATH_EXTRA >=
+		_TINYDIR_PATH_MAX)
+	{
+		/* the path for the file will be too long */
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+	if (strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) >= _TINYDIR_FILENAME_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	strcpy(file->path, dir->path);
+	strcat(file->path, "/");
+	strcpy(file->name,
+#ifdef _MSC_VER
+		dir->_f.cFileName
+#else
+		dir->_e->d_name
+#endif
+	);
+	strcat(file->path, file->name);
+#ifndef _MSC_VER
+	if (stat(file->path, &file->_s) == -1)
+	{
+		return -1;
+	}
+#endif
+	file->is_dir =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+		S_ISDIR(file->_s.st_mode);
+#endif
+	file->is_reg =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+		(
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+		S_ISREG(file->_s.st_mode);
+#endif
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+	char path[_TINYDIR_PATH_MAX];
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files || !dir->_files[i].is_dir)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	strcpy(path, dir->_files[i].path);
+	tinydir_close(dir);
+	if (tinydir_open_sorted(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+	const tinydir_file *fa = (const tinydir_file *)a;
+	const tinydir_file *fb = (const tinydir_file *)b;
+	if (fa->is_dir != fb->is_dir)
+	{
+		return -(fa->is_dir - fb->is_dir);
+	}
+	return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#endif
diff --git a/console/ujpeg.cpp b/console/ujpeg.cpp
new file mode 100644
index 0000000..516292e
--- /dev/null
+++ b/console/ujpeg.cpp
@@ -0,0 +1,668 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ujpeg.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// IMPLEMENTATION SECTION                                                    //
+// you may stop reading here                                                 //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _NJ_INCLUDE_HEADER_ONLY
+
+#ifdef _MSC_VER
+    #define NJ_INLINE static __inline
+    #define NJ_FORCE_INLINE static __forceinline
+#else
+    #define NJ_INLINE static inline
+    #define NJ_FORCE_INLINE static inline
+#endif
+
+#define NJ_USE_LIBC 1
+
+#if NJ_USE_LIBC
+    #include <stdlib.h>
+    #include <string.h>
+    #define njAllocMem malloc
+    #define njFreeMem  free
+    #define njFillMem  memset
+    #define njCopyMem  memcpy
+#elif NJ_USE_WIN32
+    #include <windows.h>
+    #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
+    #define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
+    NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
+        mov edi, block
+        mov al, value
+        mov ecx, count
+        rep stosb
+    } }
+    NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
+        mov edi, dest
+        mov esi, src
+        mov ecx, count
+        rep movsb
+    } }
+#else
+    extern void* njAllocMem(int size);
+    extern void njFreeMem(void* block);
+    extern void njFillMem(void* block, unsigned char byte, int size);
+    extern void njCopyMem(void* dest, const void* src, int size);
+#endif
+
+typedef struct _nj_code {
+    unsigned char bits, code;
+} nj_vlc_code_t;
+
+typedef struct _nj_cmp {
+    int cid;
+    int ssx, ssy;
+    int width, height;
+    int stride;
+    int qtsel;
+    int actabsel, dctabsel;
+    int dcpred;
+    unsigned char *pixels;
+} nj_component_t;
+
+typedef struct _nj_ctx {
+    nj_result_t error;
+    const unsigned char *pos;
+    int size;
+    int length;
+    int width, height;
+    int mbwidth, mbheight;
+    int mbsizex, mbsizey;
+    int ncomp;
+    nj_component_t comp[3];
+    int qtused, qtavail;
+    unsigned char qtab[4][64];
+    nj_vlc_code_t vlctab[4][65536];
+    int buf, bufbits;
+    int block[64];
+    int rstinterval;
+    unsigned char *rgb;
+} nj_context_t;
+
+static nj_context_t nj;
+
+static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
+11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
+42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
+38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
+
+NJ_FORCE_INLINE unsigned char njClip(const int x) {
+    return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
+}
+
+#define W1 2841
+#define W2 2676
+#define W3 2408
+#define W5 1609
+#define W6 1108
+#define W7 565
+
+NJ_INLINE void njRowIDCT(int* blk) {
+    int x0, x1, x2, x3, x4, x5, x6, x7, x8;
+    if (!((x1 = blk[4] << 11)
+        | (x2 = blk[6])
+        | (x3 = blk[2])
+        | (x4 = blk[1])
+        | (x5 = blk[7])
+        | (x6 = blk[5])
+        | (x7 = blk[3])))
+    {
+        blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
+        return;
+    }
+    x0 = (blk[0] << 11) + 128;
+    x8 = W7 * (x4 + x5);
+    x4 = x8 + (W1 - W7) * x4;
+    x5 = x8 - (W1 + W7) * x5;
+    x8 = W3 * (x6 + x7);
+    x6 = x8 - (W3 - W5) * x6;
+    x7 = x8 - (W3 + W5) * x7;
+    x8 = x0 + x1;
+    x0 -= x1;
+    x1 = W6 * (x3 + x2);
+    x2 = x1 - (W2 + W6) * x2;
+    x3 = x1 + (W2 - W6) * x3;
+    x1 = x4 + x6;
+    x4 -= x6;
+    x6 = x5 + x7;
+    x5 -= x7;
+    x7 = x8 + x3;
+    x8 -= x3;
+    x3 = x0 + x2;
+    x0 -= x2;
+    x2 = (181 * (x4 + x5) + 128) >> 8;
+    x4 = (181 * (x4 - x5) + 128) >> 8;
+    blk[0] = (x7 + x1) >> 8;
+    blk[1] = (x3 + x2) >> 8;
+    blk[2] = (x0 + x4) >> 8;
+    blk[3] = (x8 + x6) >> 8;
+    blk[4] = (x8 - x6) >> 8;
+    blk[5] = (x0 - x4) >> 8;
+    blk[6] = (x3 - x2) >> 8;
+    blk[7] = (x7 - x1) >> 8;
+}
+
+NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
+    int x0, x1, x2, x3, x4, x5, x6, x7, x8;
+    if (!((x1 = blk[8*4] << 8)
+        | (x2 = blk[8*6])
+        | (x3 = blk[8*2])
+        | (x4 = blk[8*1])
+        | (x5 = blk[8*7])
+        | (x6 = blk[8*5])
+        | (x7 = blk[8*3])))
+    {
+        x1 = njClip(((blk[0] + 32) >> 6) + 128);
+        for (x0 = 8;  x0;  --x0) {
+            *out = (unsigned char) x1;
+            out += stride;
+        }
+        return;
+    }
+    x0 = (blk[0] << 8) + 8192;
+    x8 = W7 * (x4 + x5) + 4;
+    x4 = (x8 + (W1 - W7) * x4) >> 3;
+    x5 = (x8 - (W1 + W7) * x5) >> 3;
+    x8 = W3 * (x6 + x7) + 4;
+    x6 = (x8 - (W3 - W5) * x6) >> 3;
+    x7 = (x8 - (W3 + W5) * x7) >> 3;
+    x8 = x0 + x1;
+    x0 -= x1;
+    x1 = W6 * (x3 + x2) + 4;
+    x2 = (x1 - (W2 + W6) * x2) >> 3;
+    x3 = (x1 + (W2 - W6) * x3) >> 3;
+    x1 = x4 + x6;
+    x4 -= x6;
+    x6 = x5 + x7;
+    x5 -= x7;
+    x7 = x8 + x3;
+    x8 -= x3;
+    x3 = x0 + x2;
+    x0 -= x2;
+    x2 = (181 * (x4 + x5) + 128) >> 8;
+    x4 = (181 * (x4 - x5) + 128) >> 8;
+    *out = njClip(((x7 + x1) >> 14) + 128);  out += stride;
+    *out = njClip(((x3 + x2) >> 14) + 128);  out += stride;
+    *out = njClip(((x0 + x4) >> 14) + 128);  out += stride;
+    *out = njClip(((x8 + x6) >> 14) + 128);  out += stride;
+    *out = njClip(((x8 - x6) >> 14) + 128);  out += stride;
+    *out = njClip(((x0 - x4) >> 14) + 128);  out += stride;
+    *out = njClip(((x3 - x2) >> 14) + 128);  out += stride;
+    *out = njClip(((x7 - x1) >> 14) + 128);
+}
+
+#define njThrow(e) do { nj.error = e; return; } while (0)
+#define njCheckError() do { if (nj.error) return; } while (0)
+
+static int njShowBits(int bits) {
+    unsigned char newbyte;
+    if (!bits) return 0;
+    while (nj.bufbits < bits) {
+        if (nj.size <= 0) {
+            nj.buf = (nj.buf << 8) | 0xFF;
+            nj.bufbits += 8;
+            continue;
+        }
+        newbyte = *nj.pos++;
+        nj.size--;
+        nj.bufbits += 8;
+        nj.buf = (nj.buf << 8) | newbyte;
+        if (newbyte == 0xFF) {
+            if (nj.size) {
+                unsigned char marker = *nj.pos++;
+                nj.size--;
+                switch (marker) {
+                    case 0x00:
+                    case 0xFF:
+                        break;
+                    case 0xD9: nj.size = 0; break;
+                    default:
+                        if ((marker & 0xF8) != 0xD0)
+                            nj.error = NJ_SYNTAX_ERROR;
+                        else {
+                            nj.buf = (nj.buf << 8) | marker;
+                            nj.bufbits += 8;
+                        }
+                }
+            } else
+                nj.error = NJ_SYNTAX_ERROR;
+        }
+    }
+    return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1);
+}
+
+NJ_INLINE void njSkipBits(int bits) {
+    if (nj.bufbits < bits)
+        (void) njShowBits(bits);
+    nj.bufbits -= bits;
+}
+
+NJ_INLINE int njGetBits(int bits) {
+    int res = njShowBits(bits);
+    njSkipBits(bits);
+    return res;
+}
+
+NJ_INLINE void njByteAlign(void) {
+    nj.bufbits &= 0xF8;
+}
+
+static void njSkip(int count) {
+    nj.pos += count;
+    nj.size -= count;
+    nj.length -= count;
+    if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR;
+}
+
+NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
+    return (pos[0] << 8) | pos[1];
+}
+
+static void njDecodeLength(void) {
+    if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR);
+    nj.length = njDecode16(nj.pos);
+    if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR);
+    njSkip(2);
+}
+
+NJ_INLINE void njSkipMarker(void) {
+    njDecodeLength();
+    njSkip(nj.length);
+}
+
+NJ_INLINE void njDecodeSOF(void) {
+    int i, ssxmax = 0, ssymax = 0;
+    nj_component_t* c;
+    njDecodeLength();
+    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);
+    nj.ncomp = nj.pos[5];
+    njSkip(6);
+    switch (nj.ncomp) {
+        case 1:
+        case 3:
+            break;
+        default:
+            njThrow(NJ_UNSUPPORTED);
+    }
+    if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR);
+    for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c) {
+        c->cid = nj.pos[0];
+        if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR);
+        if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED);  // non-power of two
+        if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR);
+        if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED);  // non-power of two
+        if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR);
+        njSkip(3);
+        nj.qtused |= 1 << c->qtsel;
+        if (c->ssx > ssxmax) ssxmax = c->ssx;
+        if (c->ssy > ssymax) ssymax = c->ssy;
+    }
+    if (nj.ncomp == 1) {
+        c = nj.comp;
+        c->ssx = c->ssy = ssxmax = ssymax = 1;
+    }
+    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;
+        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 (nj.ncomp == 3) {
+        nj.rgb = (unsigned char *)njAllocMem(nj.width * nj.height * nj.ncomp);
+        if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
+    }
+    njSkip(nj.length);
+}
+
+NJ_INLINE void njDecodeDHT(void) {
+    int codelen, currcnt, remain, spread, i, j;
+    nj_vlc_code_t *vlc;
+    static unsigned char counts[16];
+    njDecodeLength();
+    while (nj.length >= 17) {
+        i = nj.pos[0];
+        if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
+        if (i & 0x02) njThrow(NJ_UNSUPPORTED);
+        i = (i | (i >> 3)) & 3;  // combined DC/AC + tableid value
+        for (codelen = 1;  codelen <= 16;  ++codelen)
+            counts[codelen - 1] = nj.pos[codelen];
+        njSkip(17);
+        vlc = &nj.vlctab[i][0];
+        remain = spread = 65536;
+        for (codelen = 1;  codelen <= 16;  ++codelen) {
+            spread >>= 1;
+            currcnt = counts[codelen - 1];
+            if (!currcnt) continue;
+            if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR);
+            remain -= currcnt << (16 - codelen);
+            if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
+            for (i = 0;  i < currcnt;  ++i) {
+                unsigned char code = nj.pos[i];
+                for (j = spread;  j;  --j) {
+                    vlc->bits = (unsigned char) codelen;
+                    vlc->code = code;
+                    ++vlc;
+                }
+            }
+            njSkip(currcnt);
+        }
+        while (remain--) {
+            vlc->bits = 0;
+            ++vlc;
+        }
+    }
+    if (nj.length) njThrow(NJ_SYNTAX_ERROR);
+}
+
+NJ_INLINE void njDecodeDQT(void) {
+    int i;
+    unsigned char *t;
+    njDecodeLength();
+    while (nj.length >= 65) {
+        i = nj.pos[0];
+        if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
+        nj.qtavail |= 1 << i;
+        t = &nj.qtab[i][0];
+        for (i = 0;  i < 64;  ++i)
+            t[i] = nj.pos[i + 1];
+        njSkip(65);
+    }
+    if (nj.length) njThrow(NJ_SYNTAX_ERROR);
+}
+
+NJ_INLINE void njDecodeDRI(void) {
+    njDecodeLength();
+    if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
+    nj.rstinterval = njDecode16(nj.pos);
+    njSkip(nj.length);
+}
+
+static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) {
+    int value = njShowBits(16);
+    int bits = vlc[value].bits;
+    if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; }
+    njSkipBits(bits);
+    value = vlc[value].code;
+    if (code) *code = (unsigned char) value;
+    bits = value & 15;
+    if (!bits) return 0;
+    value = njGetBits(bits);
+    if (value < (1 << (bits - 1)))
+        value += ((-1) << bits) + 1;
+    return value;
+}
+
+NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) {
+    unsigned char code = 0;
+    int value, coef = 0;
+    njFillMem(nj.block, 0, sizeof(nj.block));
+    c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL);
+    nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0];
+    do {
+        value = njGetVLC(&nj.vlctab[c->actabsel][0], &code);
+        if (!code) break;  // EOB
+        if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR);
+        coef += (code >> 4) + 1;
+        if (coef > 63) njThrow(NJ_SYNTAX_ERROR);
+        nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef];
+    } while (coef < 63);
+    for (coef = 0;  coef < 64;  coef += 8)
+        njRowIDCT(&nj.block[coef]);
+    for (coef = 0;  coef < 8;  ++coef)
+        njColIDCT(&nj.block[coef], &out[coef], c->stride);
+}
+
+NJ_INLINE void njDecodeScan(void) {
+    int i, mbx, mby, sbx, sby;
+    int rstcount = nj.rstinterval, nextrst = 0;
+    nj_component_t* c;
+    njDecodeLength();
+    if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
+    if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
+    njSkip(1);
+    for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c) {
+        if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR);
+        if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR);
+        c->dctabsel = nj.pos[1] >> 4;
+        c->actabsel = (nj.pos[1] & 1) | 2;
+        njSkip(2);
+    }
+    if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED);
+    njSkip(nj.length);
+    for (mbx = mby = 0;;) {
+        for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c)
+            for (sby = 0;  sby < c->ssy;  ++sby)
+                for (sbx = 0;  sbx < c->ssx;  ++sbx) {
+                    njDecodeBlock(c, &c->pixels[((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx) << 3]);
+                    njCheckError();
+                }
+        if (++mbx >= nj.mbwidth) {
+            mbx = 0;
+            if (++mby >= nj.mbheight) break;
+        }
+        if (nj.rstinterval && !(--rstcount)) {
+            njByteAlign();
+            i = njGetBits(16);
+            if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR);
+            nextrst = (nextrst + 1) & 7;
+            rstcount = nj.rstinterval;
+            for (i = 0;  i < 3;  ++i)
+                nj.comp[i].dcpred = 0;
+        }
+    }
+    nj.error = __NJ_FINISHED;
+}
+
+#if NJ_CHROMA_FILTER
+
+#define CF4A (-9)
+#define CF4B (111)
+#define CF4C (29)
+#define CF4D (-3)
+#define CF3A (28)
+#define CF3B (109)
+#define CF3C (-9)
+#define CF3X (104)
+#define CF3Y (27)
+#define CF3Z (-3)
+#define CF2A (139)
+#define CF2B (-11)
+#define CF(x) njClip(((x) + 64) >> 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);
+    if (!out) njThrow(NJ_OUT_OF_MEM);
+    lin = c->pixels;
+    lout = out;
+    for (y = c->height;  y;  --y) {
+        lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]);
+        lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]);
+        lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]);
+        for (x = 0;  x < xmax;  ++x) {
+            lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]);
+            lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]);
+        }
+        lin += c->stride;
+        lout += c->width << 1;
+        lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]);
+        lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]);
+        lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]);
+    }
+    c->width <<= 1;
+    c->stride = c->width;
+    njFreeMem(c->pixels);
+    c->pixels = out;
+}
+
+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);
+    if (!out) njThrow(NJ_OUT_OF_MEM);
+    for (x = 0;  x < w;  ++x) {
+        cin = &c->pixels[x];
+        cout = &out[x];
+        *cout = CF(CF2A * cin[0] + CF2B * cin[s1]);  cout += w;
+        *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]);  cout += w;
+        *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]);  cout += w;
+        cin += s1;
+        for (y = c->height - 3;  y;  --y) {
+            *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]);  cout += w;
+            *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]);  cout += w;
+            cin += s1;
+        }
+        cin += s1;
+        *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]);  cout += w;
+        *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]);  cout += w;
+        *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]);
+    }
+    c->height <<= 1;
+    c->stride = c->width;
+    njFreeMem(c->pixels);
+    c->pixels = out;
+}
+
+#else
+
+NJ_INLINE void njUpsample(nj_component_t* c) {
+    int x, y, xshift = 0, yshift = 0;
+    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);
+    if (!out) njThrow(NJ_OUT_OF_MEM);
+    //lin = c->pixels;
+    lout = out;
+    for (y = 0;  y < c->height;  ++y) {
+        lin = &c->pixels[(y >> yshift) * c->stride];
+        for (x = 0;  x < c->width;  ++x)
+            lout[x] = lin[x >> xshift];
+        lout += c->width;
+    }
+    c->stride = c->width;
+    njFreeMem(c->pixels);
+    c->pixels = out;
+}
+
+#endif
+
+NJ_INLINE void njConvert() {
+    int i;
+    nj_component_t* c;
+    for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c) {
+        #if NJ_CHROMA_FILTER
+            while ((c->width < nj.width) || (c->height < nj.height)) {
+                if (c->width < nj.width) njUpsampleH(c);
+                njCheckError();
+                if (c->height < nj.height) njUpsampleV(c);
+                njCheckError();
+            }
+        #else
+            if ((c->width < nj.width) || (c->height < nj.height))
+                njUpsample(c);
+        #endif
+        if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR);
+    }
+    if (nj.ncomp == 3) {
+        // convert to RGB
+        int x, yy;
+        unsigned char *prgb = nj.rgb;
+        const unsigned char *py  = nj.comp[0].pixels;
+        const unsigned char *pcb = nj.comp[1].pixels;
+        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;
+                *prgb++ = njClip((y            + 359 * cr + 128) >> 8);
+                *prgb++ = njClip((y -  88 * cb - 183 * cr + 128) >> 8);
+                *prgb++ = njClip((y + 454 * cb            + 128) >> 8);
+            }
+            py += nj.comp[0].stride;
+            pcb += nj.comp[1].stride;
+            pcr += nj.comp[2].stride;
+        }
+    } else if (nj.comp[0].width != nj.comp[0].stride) {
+        // grayscale -> only remove stride
+        unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
+        unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
+        int y;
+        for (y = nj.comp[0].height - 1;  y;  --y) {
+            njCopyMem(pout, pin, nj.comp[0].width);
+            pin += nj.comp[0].stride;
+            pout += nj.comp[0].width;
+        }
+        nj.comp[0].stride = nj.comp[0].width;
+    }
+}
+
+void njInit(void) {
+    njFillMem(&nj, 0, sizeof(nj_context_t));
+}
+
+void njDone(void) {
+    int i;
+    for (i = 0;  i < 3;  ++i)
+        if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels);
+    if (nj.rgb) njFreeMem((void*) nj.rgb);
+    njInit();
+}
+
+nj_result_t njDecode(const void* jpeg, const int size) {
+    njDone();
+    nj.pos = (const unsigned char*) jpeg;
+    nj.size = size & 0x7FFFFFFF;
+    if (nj.size < 2) return NJ_NO_JPEG;
+    if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG;
+    njSkip(2);
+    while (!nj.error) {
+        if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR;
+        njSkip(2);
+        switch (nj.pos[-1]) {
+            case 0xC0: njDecodeSOF();  break;
+            case 0xC4: njDecodeDHT();  break;
+            case 0xDB: njDecodeDQT();  break;
+            case 0xDD: njDecodeDRI();  break;
+            case 0xDA: njDecodeScan(); break;
+            case 0xFE: njSkipMarker(); break;
+            default:
+                if ((nj.pos[-1] & 0xF0) == 0xE0)
+                    njSkipMarker();
+                else
+                    return NJ_UNSUPPORTED;
+        }
+    }
+    if (nj.error != __NJ_FINISHED) return nj.error;
+    nj.error = NJ_OK;
+    njConvert();
+    return nj.error;
+}
+
+int njGetWidth(void)            { return nj.width; }
+int njGetHeight(void)           { return nj.height; }
+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
diff --git a/console/ujpeg.h b/console/ujpeg.h
new file mode 100644
index 0000000..9321970
--- /dev/null
+++ b/console/ujpeg.h
@@ -0,0 +1,69 @@
+#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
+    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);
+    
+#ifdef  __cplusplus
+}
+#endif
+
+#endif//_NANOJPEG_H
diff --git a/console/unused/untgz.c b/console/unused/untgz.c
new file mode 100644
index 0000000..6106699
--- /dev/null
+++ b/console/unused/untgz.c
@@ -0,0 +1,929 @@
+#include "untgz.h"
+/* external functions and related types and constants */
+#include <stdio.h>          /* fprintf() */
+#include <stdlib.h>         /* malloc(), free() */
+#include <string.h>         /* strerror(), strcmp(), strlen(), memcpy() */
+#include <errno.h>          /* errno */
+#include <fcntl.h>          /* open() */
+#include <unistd.h>         /* read(), write(), close(), chown(), unlink() */
+#include <sys/types.h>
+#include <sys/stat.h>       /* stat(), chmod() */
+#include <utime.h>          /* utime() */
+#include "zlib.h"           /* inflateBackInit(), inflateBack(), */
+/* inflateBackEnd(), crc32() */
+
+#if defined(ZLIB_CONST) && !defined(z_const) //for backwards compatibility with zlib 1.2.5 (included with OSX)
+#  define z_const const
+#else
+#  define z_const
+#endif
+
+/* function declaration */
+#define local static
+
+/* buffer constants */
+#define SIZE 32768U         /* input and output buffer sizes */
+#define PIECE 16384         /* limits i/o chunks for 16-bit int case */
+
+
+
+/* structure for infback() to pass to input function in() -- it maintains the
+ input file and a buffer of size SIZE */
+struct ind {
+    int infile;
+    unsigned char *inbuf;
+};
+
+/* Load input buffer, assumed to be empty, and return bytes loaded and a
+ pointer to them.  read() is called until the buffer is full, or until it
+ returns end-of-file or error.  Return 0 on error. */
+local unsigned in(void *in_desc, z_const unsigned char **buf)
+{
+    int ret;
+    unsigned len;
+    unsigned char *next;
+    struct ind *me = (struct ind *)in_desc;
+    
+    next = me->inbuf;
+    *buf = next;
+    len = 0;
+    do {
+        ret = PIECE;
+        if ((unsigned)ret > SIZE - len)
+            ret = (int)(SIZE - len);
+        ret = (int)read(me->infile, next, ret);
+        if (ret == -1) {
+            len = 0;
+            break;
+        }
+        next += ret;
+        len += ret;
+    } while (ret != 0 && len < SIZE);
+    return len;
+}
+
+/* structure for infback() to pass to output function out() -- it maintains the
+ output file, a running CRC-32 check on the output and the total number of
+ bytes output, both for checking against the gzip trailer.  (The length in
+ the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
+ the output is greater than 4 GB.) */
+struct outd {
+    int outfile;
+    int check;                  /* true if checking crc and total */
+    unsigned long crc;
+    unsigned long total;
+};
+
+/* Write output buffer and update the CRC-32 and total bytes written.  write()
+ is called until all of the output is written or an error is encountered.
+ On success out() returns 0.  For a write failure, out() returns 1.  If the
+ output file descriptor is -1, then nothing is written.
+ */
+local int out(void *out_desc, unsigned char *buf, unsigned len)
+{
+    int ret;
+    struct outd *me = (struct outd *)out_desc;
+    
+    if (me->check) {
+        me->crc = crc32(me->crc, buf, len);
+        me->total += len;
+    }
+    if (me->outfile != -1)
+        do {
+            ret = PIECE;
+            if ((unsigned)ret > len)
+                ret = (int)len;
+            ret = (int)write(me->outfile, buf, ret);
+            if (ret == -1)
+                return 1;
+            buf += ret;
+            len -= ret;
+        } while (len != 0);
+    return 0;
+}
+
+/* next input byte macro for use inside lunpipe() and gunpipe() */
+#define NEXT() (have ? 0 : (have = in(indp, &next)), \
+last = have ? (have--, (int)(*next++)) : -1)
+
+/* memory for gunpipe() and lunpipe() --
+ the first 256 entries of prefix[] and suffix[] are never used, could
+ have offset the index, but it's faster to waste the memory */
+unsigned char inbuf[SIZE];              /* input buffer */
+unsigned char outbuf[SIZE];             /* output buffer */
+unsigned short prefix[65536];           /* index to LZW prefix string */
+unsigned char suffix[65536];            /* one-character LZW suffix */
+unsigned char match[65280 + 2];         /* buffer for reversed match or gzip
+                                         32K sliding window */
+
+/* throw out what's left in the current bits byte buffer (this is a vestigial
+ aspect of the compressed data format derived from an implementation that
+ made use of a special VAX machine instruction!) */
+#define FLUSHCODE() \
+do { \
+left = 0; \
+rem = 0; \
+if (chunk > have) { \
+chunk -= have; \
+have = 0; \
+if (NEXT() == -1) \
+break; \
+chunk--; \
+if (chunk > have) { \
+chunk = have = 0; \
+break; \
+} \
+} \
+have -= chunk; \
+next += chunk; \
+chunk = 0; \
+} while (0)
+
+/* Decompress a compress (LZW) file from indp to outfile.  The compress magic
+ header (two bytes) has already been read and verified.  There are have bytes
+ of buffered input at next.  strm is used for passing error information back
+ to gunpipe().
+ 
+ lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
+ file, read error, or write error (a write error indicated by strm->next_in
+ not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
+ */
+local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp,
+                  int outfile, z_stream *strm)
+{
+    int last;                   /* last byte read by NEXT(), or -1 if EOF */
+    unsigned chunk;             /* bytes left in current chunk */
+    int left;                   /* bits left in rem */
+    unsigned rem;               /* unused bits from input */
+    int bits;                   /* current bits per code */
+    unsigned code;              /* code, table traversal index */
+    unsigned mask;              /* mask for current bits codes */
+    int max;                    /* maximum bits per code for this stream */
+    unsigned flags;             /* compress flags, then block compress flag */
+    unsigned end;               /* last valid entry in prefix/suffix tables */
+    unsigned temp;              /* current code */
+    unsigned prev;              /* previous code */
+    unsigned final;             /* last character written for previous code */
+    unsigned stack;             /* next position for reversed string */
+    unsigned outcnt;            /* bytes in output buffer */
+    struct outd outd;           /* output structure */
+    unsigned char *p;
+    
+    /* set up output */
+    outd.outfile = outfile;
+    outd.check = 0;
+    
+    /* process remainder of compress header -- a flags byte */
+    flags = NEXT();
+    if (last == -1)
+        return Z_BUF_ERROR;
+    if (flags & 0x60) {
+        strm->msg = (char *)"unknown lzw flags set";
+        return Z_DATA_ERROR;
+    }
+    max = flags & 0x1f;
+    if (max < 9 || max > 16) {
+        strm->msg = (char *)"lzw bits out of range";
+        return Z_DATA_ERROR;
+    }
+    if (max == 9)                           /* 9 doesn't really mean 9 */
+        max = 10;
+    flags &= 0x80;                          /* true if block compress */
+    
+    /* clear table */
+    bits = 9;
+    mask = 0x1ff;
+    end = flags ? 256 : 255;
+    
+    /* set up: get first 9-bit code, which is the first decompressed byte, but
+     don't create a table entry until the next code */
+    if (NEXT() == -1)                       /* no compressed data is ok */
+        return Z_OK;
+    final = prev = (unsigned)last;          /* low 8 bits of code */
+    if (NEXT() == -1)                       /* missing a bit */
+        return Z_BUF_ERROR;
+    if (last & 1) {                         /* code must be < 256 */
+        strm->msg = (char *)"invalid lzw code";
+        return Z_DATA_ERROR;
+    }
+    rem = (unsigned)last >> 1;              /* remaining 7 bits */
+    left = 7;
+    chunk = bits - 2;                       /* 7 bytes left in this chunk */
+    outbuf[0] = (unsigned char)final;       /* write first decompressed byte */
+    outcnt = 1;
+    
+    /* decode codes */
+    stack = 0;
+    for (;;) {
+        /* if the table will be full after this, increment the code size */
+        if (end >= mask && bits < max) {
+            FLUSHCODE();
+            bits++;
+            mask <<= 1;
+            mask++;
+        }
+        
+        /* get a code of length bits */
+        if (chunk == 0)                     /* decrement chunk modulo bits */
+            chunk = bits;
+        code = rem;                         /* low bits of code */
+        if (NEXT() == -1) {                 /* EOF is end of compressed data */
+            /* write remaining buffered output */
+            if (outcnt && out(&outd, outbuf, outcnt)) {
+                strm->next_in = outbuf;     /* signal write error */
+                return Z_BUF_ERROR;
+            }
+            return Z_OK;
+        }
+        code += (unsigned)last << left;     /* middle (or high) bits of code */
+        left += 8;
+        chunk--;
+        if (bits > left) {                  /* need more bits */
+            if (NEXT() == -1)               /* can't end in middle of code */
+                return Z_BUF_ERROR;
+            code += (unsigned)last << left; /* high bits of code */
+            left += 8;
+            chunk--;
+        }
+        code &= mask;                       /* mask to current code length */
+        left -= bits;                       /* number of unused bits */
+        rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
+        
+        /* process clear code (256) */
+        if (code == 256 && flags) {
+            FLUSHCODE();
+            bits = 9;                       /* initialize bits and mask */
+            mask = 0x1ff;
+            end = 255;                      /* empty table */
+            continue;                       /* get next code */
+        }
+        
+        /* special code to reuse last match */
+        temp = code;                        /* save the current code */
+        if (code > end) {
+            /* Be picky on the allowed code here, and make sure that the code
+             we drop through (prev) will be a valid index so that random
+             input does not cause an exception.  The code != end + 1 check is
+             empirically derived, and not checked in the original uncompress
+             code.  If this ever causes a problem, that check could be safely
+             removed.  Leaving this check in greatly improves gun's ability
+             to detect random or corrupted input after a compress header.
+             In any case, the prev > end check must be retained. */
+            if (code != end + 1 || prev > end) {
+                strm->msg = (char *)"invalid lzw code";
+                return Z_DATA_ERROR;
+            }
+            match[stack++] = (unsigned char)final;
+            code = prev;
+        }
+        
+        /* walk through linked list to generate output in reverse order */
+        p = match + stack;
+        while (code >= 256) {
+            *p++ = suffix[code];
+            code = prefix[code];
+        }
+        stack = p - match;
+        match[stack++] = (unsigned char)code;
+        final = code;
+        
+        /* link new table entry */
+        if (end < mask) {
+            end++;
+            prefix[end] = (unsigned short)prev;
+            suffix[end] = (unsigned char)final;
+        }
+        
+        /* set previous code for next iteration */
+        prev = temp;
+        
+        /* write output in forward order */
+        while (stack > SIZE - outcnt) {
+            while (outcnt < SIZE)
+                outbuf[outcnt++] = match[--stack];
+            if (out(&outd, outbuf, outcnt)) {
+                strm->next_in = outbuf; /* signal write error */
+                return Z_BUF_ERROR;
+            }
+            outcnt = 0;
+        }
+        p = match + stack;
+        do {
+            outbuf[outcnt++] = *--p;
+        } while (p > match);
+        stack = 0;
+        
+        /* loop for next code with final and prev as the last match, rem and
+         left provide the first 0..7 bits of the next code, end is the last
+         valid table entry */
+    }
+}
+
+/* Decompress a gzip file from infile to outfile.  strm is assumed to have been
+ successfully initialized with inflateBackInit().  The input file may consist
+ of a series of gzip streams, in which case all of them will be decompressed
+ to the output file.  If outfile is -1, then the gzip stream(s) integrity is
+ checked and nothing is written.
+ 
+ The return value is a zlib error code: Z_MEM_ERROR if out of memory,
+ Z_DATA_ERROR if the header or the compressed data is invalid, or if the
+ trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
+ prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
+ stream) follows a valid gzip stream.
+ */
+local int gunpipe(z_stream *strm, int infile, int outfile)
+{
+    int ret, first, last;
+    unsigned have, flags, len;
+    z_const unsigned char *next = NULL;
+    struct ind ind, *indp;
+    struct outd outd;
+    
+    /* setup input buffer */
+    ind.infile = infile;
+    ind.inbuf = inbuf;
+    indp = &ind;
+    
+    /* decompress concatenated gzip streams */
+    have = 0;                               /* no input data read in yet */
+    first = 1;                              /* looking for first gzip header */
+    strm->next_in = Z_NULL;                 /* so Z_BUF_ERROR means EOF */
+    for (;;) {
+        /* look for the two magic header bytes for a gzip stream */
+        if (NEXT() == -1) {
+            ret = Z_OK;
+            break;                          /* empty gzip stream is ok */
+        }
+        if (last != 31 || (NEXT() != 139 && last != 157)) {
+            strm->msg = (char *)"incorrect header check";
+            ret = first ? Z_DATA_ERROR : Z_ERRNO;
+            break;                          /* not a gzip or compress header */
+        }
+        first = 0;                          /* next non-header is junk */
+        
+        /* process a compress (LZW) file -- can't be concatenated after this */
+        if (last == 157) {
+            ret = lunpipe(have, next, indp, outfile, strm);
+            break;
+        }
+        
+        /* process remainder of gzip header */
+        ret = Z_BUF_ERROR;
+        if (NEXT() != 8) {                  /* only deflate method allowed */
+            if (last == -1) break;
+            strm->msg = (char *)"unknown compression method";
+            ret = Z_DATA_ERROR;
+            break;
+        }
+        flags = NEXT();                     /* header flags */
+        NEXT();                             /* discard mod time, xflgs, os */
+        NEXT();
+        NEXT();
+        NEXT();
+        NEXT();
+        NEXT();
+        if (last == -1) break;
+        if (flags & 0xe0) {
+            strm->msg = (char *)"unknown header flags set";
+            ret = Z_DATA_ERROR;
+            break;
+        }
+        if (flags & 4) {                    /* extra field */
+            len = NEXT();
+            len += (unsigned)(NEXT()) << 8;
+            if (last == -1) break;
+            while (len > have) {
+                len -= have;
+                have = 0;
+                if (NEXT() == -1) break;
+                len--;
+            }
+            if (last == -1) break;
+            have -= len;
+            next += len;
+        }
+        if (flags & 8)                      /* file name */
+            while (NEXT() != 0 && last != -1)
+                ;
+        if (flags & 16)                     /* comment */
+            while (NEXT() != 0 && last != -1)
+                ;
+        if (flags & 2) {                    /* header crc */
+            NEXT();
+            NEXT();
+        }
+        if (last == -1) break;
+        
+        /* set up output */
+        outd.outfile = outfile;
+        outd.check = 1;
+        outd.crc = crc32(0L, Z_NULL, 0);
+        outd.total = 0;
+        
+        /* decompress data to output */
+        strm->next_in = next;
+        strm->avail_in = have;
+        ret = inflateBack(strm, in, indp, out, &outd);
+        if (ret != Z_STREAM_END) break;
+        next = strm->next_in;
+        have = strm->avail_in;
+        strm->next_in = Z_NULL;             /* so Z_BUF_ERROR means EOF */
+        
+        /* check trailer */
+        ret = Z_BUF_ERROR;
+        if (NEXT() != (int)(outd.crc & 0xff) ||
+            NEXT() != (int)((outd.crc >> 8) & 0xff) ||
+            NEXT() != (int)((outd.crc >> 16) & 0xff) ||
+            NEXT() != (int)((outd.crc >> 24) & 0xff)) {
+            /* crc error */
+            if (last != -1) {
+                strm->msg = (char *)"incorrect data check";
+                ret = Z_DATA_ERROR;
+            }
+            break;
+        }
+        if (NEXT() != (int)(outd.total & 0xff) ||
+            NEXT() != (int)((outd.total >> 8) & 0xff) ||
+            NEXT() != (int)((outd.total >> 16) & 0xff) ||
+            NEXT() != (int)((outd.total >> 24) & 0xff)) {
+            /* length error */
+            if (last != -1) {
+                strm->msg = (char *)"incorrect length check";
+                ret = Z_DATA_ERROR;
+            }
+            break;
+        }
+        
+        /* go back and look for another gzip stream */
+    }
+    
+    /* clean up and return */
+    return ret;
+}
+
+#if defined(_WIN64) || defined(_WIN32)
+local void copymeta(char *from, char *to)
+{
+ //not for windows computers
+}
+#else
+
+/* Copy file attributes, from -> to, as best we can.  This is best effort, so
+ no errors are reported.  The mode bits, including suid, sgid, and the sticky
+ bit are copied (if allowed), the owner's user id and group id are copied
+ (again if allowed), and the access and modify times are copied. */
+local void copymeta(char *from, char *to)
+{
+    struct stat was;
+    struct utimbuf when;
+    
+    /* get all of from's Unix meta data, return if not a regular file */
+    if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
+        return;
+    
+    /* set to's mode bits, ignore errors */
+    (void)chmod(to, was.st_mode & 07777);
+    
+    /* copy owner's user and group, ignore errors */
+    (void)chown(to, was.st_uid, was.st_gid);
+    
+    /* copy access and modify times, ignore errors */
+    when.actime = was.st_atime;
+    when.modtime = was.st_mtime;
+    (void)utime(to, &when);
+}
+#endif
+
+/* Decompress the file inname to the file outnname, of if test is true, just
+ decompress without writing and check the gzip trailer for integrity.  If
+ inname is NULL or an empty string, read from stdin.  If outname is NULL or
+ an empty string, write to stdout.  strm is a pre-initialized inflateBack
+ structure.  When appropriate, copy the file attributes from inname to
+ outname.
+ 
+ gunzip() returns 1 if there is an out-of-memory error or an unexpected
+ return code from gunpipe().  Otherwise it returns 0.
+ */
+local int gunzip(z_stream *strm, char *inname, char *outname, int test, int deletein)
+{
+    int ret;
+    int infile, outfile;
+    
+    /* open files */
+    if (inname == NULL || *inname == 0) {
+        inname = "-";
+        infile = 0;     /* stdin */
+    }
+    else {
+        infile = open(inname, O_RDONLY, 0);
+        if (infile == -1) {
+            fprintf(stderr, "gun cannot open %s\n", inname);
+            return -1;
+        }
+    }
+    if (test)
+        outfile = -1;
+    else if (outname == NULL || *outname == 0) {
+        outname = "-";
+        outfile = 1;    /* stdout */
+    }
+    else {
+        outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+        if (outfile == -1) {
+            close(infile);
+            fprintf(stderr, "gun cannot create %s\n", outname);
+            return -1;
+        }
+    }
+    errno = 0;
+    
+    /* decompress */
+    ret = gunpipe(strm, infile, outfile);
+    if (outfile > 2) close(outfile);
+    if (infile > 2) close(infile);
+    
+    /* interpret result */
+    switch (ret) {
+        case Z_OK:
+        case Z_ERRNO:
+            if (infile > 2 && outfile > 2) {
+                copymeta(inname, outname);          /* copy attributes */
+                if (deletein) unlink(inname);
+            }
+            if (ret == Z_ERRNO)
+                fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
+                        inname);
+            break;
+        case Z_DATA_ERROR:
+            if (outfile > 2) unlink(outname);
+            fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
+            break;
+        case Z_MEM_ERROR:
+            if (outfile > 2) unlink(outname);
+            fprintf(stderr, "gun out of memory error--aborting\n");
+            return 1;
+        case Z_BUF_ERROR:
+            if (outfile > 2) unlink(outname);
+            if (strm->next_in != Z_NULL) {
+                fprintf(stderr, "gun write error on %s: %s\n",
+                        outname, strerror(errno));
+            }
+            else if (errno) {
+                fprintf(stderr, "gun read error on %s: %s\n",
+                        inname, strerror(errno));
+            }
+            else {
+                fprintf(stderr, "gun unexpected end of file on '%s'\n",
+                        inname);
+            }
+            break;
+        default:
+            if (outfile > 2) unlink(outname);
+            fprintf(stderr, "gun internal error--aborting\n");
+            return 1;
+    }
+    return ret;
+}
+
+
+/*
+ * "untar" is an extremely simple tar extractor:
+ *  * A single C source file, so it should be easy to compile
+ *    and run on any system with a C compiler.
+ *  * Extremely portable standard C.  The only non-ANSI function
+ *    used is mkdir().
+ *  * Reads basic ustar tar archives.
+ *  * Does not require libarchive or any other special library.
+ *
+ * To compile: cc -o untar untar.c
+ *
+ * Usage:  untar <archive>
+ *
+ * In particular, this program should be sufficient to extract the
+ * distribution for libarchive, allowing people to bootstrap
+ * libarchive on systems that do not already have a tar program.
+ *
+ * To unpack libarchive-x.y.z.tar.gz:
+ *    * gunzip libarchive-x.y.z.tar.gz
+ *    * untar libarchive-x.y.z.tar
+ *
+ * Written by Tim Kientzle, March 2009.
+ *
+ * Released into the public domain.
+ */
+
+/* These are all highly standard and portable headers. */
+/* g++ -O3 -lz untar.c  -s -o untar */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* This is for mkdir(); this may need to be changed for some platforms. */
+#include <sys/stat.h>  /* For mkdir() */
+
+/* Parse an octal number, ignoring leading and trailing nonsense. */
+static int
+parseoct(const char *p, size_t n)
+{
+	int i = 0;
+    
+	while (*p < '0' || *p > '7') {
+		++p;
+		--n;
+	}
+	while (*p >= '0' && *p <= '7' && n > 0) {
+		i *= 8;
+		i += *p - '0';
+		++p;
+		--n;
+	}
+	return (i);
+}
+
+/* Returns true if this is 512 zero bytes. */
+static int is_end_of_archive(const char *p)
+{
+	int n;
+	for (n = 511; n >= 0; --n)
+		if (p[n] != '\0')
+			return (0);
+	return (1);
+}
+
+#if defined(_WIN64) || defined(_WIN32)
+const char kPathSeparator ='\\';
+//const char kFileSep[2] = "\\";
+#else
+const char kPathSeparator ='/';
+//const char kFileSep[2] = "/";
+#endif
+
+void dropFilenameFromPathKeepFinalFileSep(char *path) { 
+    const char *dirPath = strrchr(path, kPathSeparator);
+    if (dirPath == NULL) {
+        strcpy(path,"");
+    } else
+        path[dirPath - path +1] = 0; // 
+}
+
+#if defined(_WIN64) || defined(_WIN32)
+
+bool is_fileexistsWin(const char * filename) {
+    FILE * fp = NULL;
+    if ((fp = fopen(filename, "r"))) {
+        fclose(fp);
+        return true;
+    }
+    return false;
+}
+
+int mkdirWin(char *pathname) 
+{
+	if (is_fileexistsWin(pathname)) return 0;	
+	return mkdir(pathname);
+}
+#endif
+
+/* Create a directory, including parent directories as necessary. */
+static void create_dir(char *pathname, int mode)
+{
+	char *p;
+	int r;
+    
+	/* Strip trailing '/' */
+	if (pathname[strlen(pathname) - 1] == kPathSeparator)
+		pathname[strlen(pathname) - 1] = '\0';    
+	/* Try creating the directory. */
+	#if defined(_WIN64) || defined(_WIN32)
+	r = mkdirWin(pathname);
+    	#else
+	r = mkdir(pathname, mode);
+    	#endif
+    	fprintf(stderr, "attempted  mkdir %s got %d\n", pathname, r);
+	if (r != 0) {
+		/* On failure, try creating parent directory. */
+		p = strrchr(pathname, kPathSeparator);
+		if (p != NULL) {
+			*p = '\0';
+			create_dir(pathname, 0755);
+			*p = kPathSeparator;
+			
+			#if defined(_WIN64) || defined(_WIN32)
+			r = mkdirWin(pathname);
+    			#else
+			r = mkdir(pathname, mode);
+    			#endif
+
+		}
+	}
+	if (r != 0)
+		fprintf(stderr, "Could not create directory %s\n", pathname);
+}
+
+/* Create a file, including parent directory as necessary. */
+static FILE *
+create_file(char *pathname, int mode)
+{
+	FILE *f;
+	f = fopen(pathname, "w+");
+	if (f == NULL) {
+		/* Try creating parent dir and then creating file. */
+		char *p = strrchr(pathname, kPathSeparator);
+		if (p != NULL) {
+			*p = '\0';
+			create_dir(pathname, 0755);
+			*p = kPathSeparator;
+			f = fopen(pathname, "w+");
+		}
+	}
+	return (f);
+}
+
+/* Verify the tar checksum. */
+static int
+verify_checksum(const char *p)
+{
+	int n, u = 0;
+	for (n = 0; n < 512; ++n) {
+		if (n < 148 || n > 155)
+        /* Standard tar checksum adds unsigned bytes. */
+			u += ((unsigned char *)p)[n];
+		else
+			u += 0x20;
+        
+	}
+	return (u == parseoct(p + 148, 8));
+}
+
+/* Extract a tar archive. */
+static void untar(FILE *a, const char *path)
+{
+	char buff[1024];
+	FILE *f = NULL;
+	size_t bytes_read;
+	int filesize;
+    char pathDir[1024];
+    strcpy(pathDir,path);
+    dropFilenameFromPathKeepFinalFileSep(pathDir);
+    char newFile[1024] = {""};
+	for (;;) {
+		bytes_read = fread(buff, 1, 512, a);
+		if (bytes_read < 512) {
+			fprintf(stderr,
+                    "Short read on %s: expected 512, got %zd\n",path, bytes_read);
+			return;
+		}
+		if (is_end_of_archive(buff)) {
+			printf("End of %s\n", path);
+			return;
+		}
+		if (!verify_checksum(buff)) {
+			fprintf(stderr, "Checksum failure\n");
+			return;
+		}
+		filesize = parseoct(buff + 124, 12);
+		switch (buff[156]) {
+            case '1':
+                printf(" Ignoring hardlink %s\n", buff);
+                break;
+            case '2':
+                printf(" Ignoring symlink %s\n", buff);
+                break;
+            case '3':
+                printf(" Ignoring character device %s\n", buff);
+				break;
+            case '4':
+                printf(" Ignoring block device %s\n", buff);
+                break;
+            case '5':
+                strcpy (newFile,pathDir);
+                strcat (newFile,buff);
+                //printf(" Extracting dir %s\n", newFile);
+                create_dir(buff, parseoct(buff + 100, 8));
+                filesize = 0;
+                break;
+            case '6':
+                printf(" Ignoring FIFO %s\n", buff);
+                break;
+            default:
+                strcpy (newFile,pathDir);
+                strcat (newFile,buff);
+                //printf(" Extracting file %s\n", newFile);
+                f = create_file(newFile, parseoct(buff + 100, 8));
+                break;
+		}
+		while (filesize > 0) {
+			bytes_read = fread(buff, 1, 512, a);
+			if (bytes_read < 512) {
+				fprintf(stderr,
+                        "Short read on %s: Expected 512, got %zd\n",path, bytes_read);
+				return;
+			}
+			if (filesize < 512)
+				bytes_read = filesize;
+			if (f != NULL) {
+				if (fwrite(buff, 1, bytes_read, f)
+				    != bytes_read)
+				{
+					fprintf(stderr, "Failed write\n");
+					fclose(f);
+					f = NULL;
+				}
+			}
+			filesize -= bytes_read;
+		}
+		if (f != NULL) {
+			fclose(f);
+			f = NULL;
+		}
+	}
+}
+
+void getFileNam( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2'
+{
+    const char *filename = strrchr(path, '/'); //UNIX
+    if (filename == 0)
+        filename = strrchr(path, '\\'); //Windows
+    //const char *filename = strrchr(path, kPathSeparator); //x
+    if (filename == NULL) {//no path separator
+        strcpy(pathParent,path);
+        return;
+    }
+    filename++;
+    strcpy(pathParent,filename);
+}
+
+void changeDirOfFilename( char *filename, char *newdir) {//set path of filename to be in outdir
+    if (strlen(newdir) < 1) return;
+    char fname[1024];
+    getFileNam( fname, filename);
+    char outname[1024];
+    strcpy(outname,newdir);
+    if  (outname[strlen(outname) - 1] != kPathSeparator)
+#if defined(_WIN64) || defined(_WIN32)
+    strcat (outname,"\\");
+#else
+    strcat (outname,"/"); //append name
+#endif
+    strcat (outname,fname); //append name
+    strcpy(filename,outname);
+}
+
+//next portion by Chris Rorden, 2014
+int untargz( char * fname, char * outdir){
+//returns 0 if success in extracting file, -666 if not a tgz/tar.gz file, otherwise error
+    //detect type
+    char tardir[1024] = "";
+    int len = (int)strlen(fname);
+    int ext = 0;
+    if (len < 4) return - 1;
+    if ((len > 7 ) && (strcmp(fname + len - 7, ".tar.gz") == 0))  {
+        ext = 7;
+    } else if (strcmp(fname + len - 4, ".tgz") == 0)  {
+        ext = 4;
+    } else if (strcmp(fname + len - 3, ".gz") == 0)  {
+            return -666;//ext = 3; //this function only converts tar.gz and tgz
+    } else
+        return -666;
+    len -= ext;
+    //set filenames
+    char outname[1024];
+    //strlcpy not included in all distros http://stackoverflow.com/questions/18547251/when-i-use-strlcpy-function-in-c-the-compilor-give-me-an-error
+    strncpy(outname, fname, len);//strlcpy(outname, fname, len+1);
+    changeDirOfFilename(outname, outdir);
+    if (ext > 3) { //extract tar.gz files to new folder
+        strcpy(tardir, outname); //extract ~/dir/f.tar to ~/dir/f
+        if( access( tardir, F_OK ) != -1 ) {
+            printf("warning folder %s already exists\n",tardir);
+            //return -1;
+        } else  create_dir(tardir, 0777);
+        strcat(outname, ".tar"); // file.tar.gz -> file.tar, file.tgz -> file.tar
+        changeDirOfFilename(outname, tardir);
+    }
+    printf("gun will copy *%s* to *%s*\n",fname,outname);
+    if( access( outname, F_OK ) != -1 ) {
+        printf("file %s already exists\n",outname);
+        return -1;
+    }
+    //un gzip
+    unsigned char *window;
+    z_stream strm;
+    window = match;  /* reuse LZW match buffer */
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    int ret = inflateBackInit(&strm, 15, window);
+    ret = gunzip(&strm, fname, outname, 0, 0);
+    inflateBackEnd(&strm);
+    if (ret != 0) return ret;
+    //un tar
+    if (ext > 3) { //.tgz or .tar.gz
+        FILE *a;
+        a = fopen(outname, "r");
+        untar(a, outname);
+        fclose(a);
+        unlink(outname);
+        strcpy(fname,tardir);
+    }
+    return ret;
+}
\ No newline at end of file
diff --git a/console/unused/untgz.h b/console/unused/untgz.h
new file mode 100644
index 0000000..eb8c5f2
--- /dev/null
+++ b/console/unused/untgz.h
@@ -0,0 +1,8 @@
+#ifndef UNTGZ_H__
+#define UNTGZ_H__
+
+//extern const char const *one_hit_wonder;
+
+int untargz( char * fname, char * outdir);
+
+#endif
\ No newline at end of file
diff --git a/cpfiles.command b/cpfiles.command
new file mode 100755
index 0000000..fd885e6
--- /dev/null
+++ b/cpfiles.command
@@ -0,0 +1,18 @@
+#!/bin/sh
+cd /Users/rorden/Documents/cocoa/dcm2niix/console
+
+#cp ni*.c ../qtGui
+#cp ni*.cpp ../qtGui
+#cp ni*.h ../qtGui
+#cp tinydir.h ../qtGui
+
+#cp ni*.c ../wxWidgets
+#cp ni*.cpp ../wxWidgets
+#cp ni*.h ../wxWidgets
+#cp tinydir.h ../wxWidgets
+
+cp *.c ../xcode/dcm2/core
+cp *.cpp ../xcode/dcm2/core
+cp *.h ../xcode/dcm2/core
+
+#myDisableMiniZ
\ No newline at end of file
diff --git a/dcm2laz/LibTar.pas b/dcm2laz/LibTar.pas
new file mode 100644
index 0000000..f0c03b9
--- /dev/null
+++ b/dcm2laz/LibTar.pas
@@ -0,0 +1,967 @@
+(**
+===============================================================================================
+Name    : LibTar
+===============================================================================================
+Subject : Handling of "tar" files
+===============================================================================================
+Author  : Stefan Heymann
+          Eschenweg 3
+          72076 T�bingen
+          GERMANY
+
+E-Mail:   stefan at destructor.de
+Web:      www.destructor.de
+
+===============================================================================================
+TTarArchive Usage
+-----------------
+- Choose a constructor
+- Make an instance of TTarArchive                  TA := TTarArchive.Create (Filename);
+- Scan through the archive                         TA.Reset;
+                                                   while TA.FindNext (DirRec) do begin
+- Evaluate the DirRec for each file                  ListBox.Items.Add (DirRec.Name);
+- Read out the current file                          TA.ReadFile (DestFilename);
+  (You can ommit this if you want to
+  read in the directory only)                        end;
+- You're done                                      TA.Free;
+
+
+TTarWriter Usage
+----------------
+- Choose a constructor
+- Make an instance of TTarWriter                   TW := TTarWriter.Create ('my.tar');
+- Add a file to the tar archive                    TW.AddFile ('foobar.txt');
+- Add a string as a file                           TW.AddString (SL.Text, 'joe.txt', Now);
+- Destroy TarWriter instance                       TW.Free;
+- Now your tar file is ready.
+
+
+Source, Legals ("Licence")
+--------------------------
+The official site to get this code is http://www.destructor.de/
+
+Usage and Distribution of this Source Code is ruled by the
+"Destructor.de Source code Licence" (DSL) which comes with this file or
+can be downloaded at http://www.destructor.de/
+
+IN SHORT: Usage and distribution of this source code is free.
+          You use it completely on your own risk.
+===============================================================================================
+!!!  All parts of this code which are not finished or known to be buggy
+     are marked with three exclamation marks
+===============================================================================================
+Date        Author Changes
+-----------------------------------------------------------------------------------------------
+2001-04-26  HeySt  0.0.1 Start
+2001-04-28  HeySt  1.0.0 First Release
+2001-06-19  HeySt  2.0.0 Finished TTarWriter
+2001-09-06  HeySt  2.0.1 Bugfix in TTarArchive.FindNext: FBytesToGo must sometimes be 0
+2001-10-25  HeySt  2.0.2 Introduced the ClearDirRec procedure
+2001-11-13  HeySt  2.0.3 Bugfix: Take out ClearDirRec call from WriteTarHeader
+                         Bug Reported by Tony BenBrahim
+2001-12-25  HeySt  2.0.4 WriteTarHeader: Fill Rec with zero bytes before filling it
+2002-05-18  HeySt  2.0.5 Kylix awareness: Thanks to Kerry L. Davison for the changes
+2005-09-03  HeySt  2.0.6 TTarArchive.FindNext: Don't access SourceStream.Size
+                         (for compressed streams, which don't know their .Size)
+2006-03-13  HeySt  2.0.7 Bugfix in ReadFile (Buffer : POINTER)
+2007-05-16  HeySt  2.0.8 Bugfix in TTarWriter.AddFile (Convertfilename in the ELSE branch)
+                         Bug Reported by Chris Rorden
+2010-11-29  HeySt  2.1.0 WriteTarHeader: Mode values for ftNormal/ftLink/ftSymbolicLink/ftDirectory
+                         Thanks to Iouri Kharon for the fix.
+                         Still no support for filenames > 100 bytes. Sorry.
+                         Support for Unicode Delphi versions (2009, 2010, XE, etc.)
+2011-05-23  HeySt  2.1.1 New IFDEF WIN32 in the USES clause
+2014-06-23  HeySt  2.1.2 64-Bit Seek operations, CurFilePos as Int64
+                         Thanks to And�dy Csaba for the fixes.
+*)
+
+unit LibTar;
+
+interface
+
+uses
+(*$IFDEF LINUX*)
+   Libc,
+(*$ENDIF *)
+{$IFDEF WIN32}
+  {$DEFINE MSWINDOWS} // predefined for D6+/BCB6+    // because in Delphi 5  MSWINDOWS is not defined
+{$ENDIF}
+(*$IFDEF MSWINDOWS *)
+   Windows,
+(*$ENDIF *)
+  SysUtils, Classes;
+
+type
+  (*$IFNDEF UNICODE *)
+  RawByteString = AnsiString;
+  (*$ENDIF *)
+
+  // --- File Access Permissions
+  TTarPermission  = (tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
+                     tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
+                     tpReadByOther, tpWriteByOther, tpExecuteByOther);
+  TTarPermissions = set of TTarPermission;
+
+  // --- Type of File
+  TFileType = (ftNormal,          // Regular file
+               ftLink,            // Link to another, previously archived, file (LinkName)
+               ftSymbolicLink,    // Symbolic link to another file              (LinkName)
+               ftCharacter,       // Character special files
+               ftBlock,           // Block special files
+               ftDirectory,       // Directory entry. Size is zero (unlimited) or max. number of bytes
+               ftFifo,            // FIFO special file. No data stored in the archive.
+               ftContiguous,      // Contiguous file, if supported by OS
+               ftDumpDir,         // List of files
+               ftMultiVolume,     // Multi-volume file part
+               ftVolumeHeader);   // Volume header. Can appear only as first record in the archive
+
+  // --- Mode
+  TTarMode  = (tmSetUid, tmSetGid, tmSaveText);
+  TTarModes = set of TTarMode;
+
+  // --- Record for a Directory Entry
+  //     Adjust the ClearDirRec procedure when this record changes!
+  TTarDirRec  = record
+                  Name        : AnsiString;        // File path and name
+                  Size        : INT64;             // File size in Bytes
+                  DateTime    : TDateTime;         // Last modification date and time
+                  Permissions : TTarPermissions;   // Access permissions
+                  FileType    : TFileType;         // Type of file
+                  LinkName    : AnsiString;        // Name of linked file (for ftLink, ftSymbolicLink)
+                  UID         : INTEGER;           // User ID
+                  GID         : INTEGER;           // Group ID
+                  UserName    : AnsiString;        // User name
+                  GroupName   : AnsiString;        // Group name
+                  ChecksumOK  : BOOLEAN;           // Checksum was OK
+                  Mode        : TTarModes;         // Mode
+                  Magic       : AnsiString;        // Contents of the "Magic" field
+                  MajorDevNo  : INTEGER;           // Major Device No. for ftCharacter and ftBlock
+                  MinorDevNo  : INTEGER;           // Minor Device No. for ftCharacter and ftBlock
+                  FilePos     : INT64;             // Position in TAR file
+                end;
+
+  // --- The TAR Archive CLASS
+  TTarArchive = class
+                protected
+                  FStream     : TStream;   // Internal Stream
+                  FOwnsStream : BOOLEAN;   // True if FStream is owned by the TTarArchive instance
+                  FBytesToGo  : INT64;     // Bytes until the next Header Record
+                public
+                  CONSTRUCTOR Create (Stream   : TStream);                                OVERLOAD;
+                  CONSTRUCTOR Create (Filename : STRING;
+                                      FileMode : WORD = fmOpenRead OR fmShareDenyWrite);  OVERLOAD;
+                  DESTRUCTOR Destroy;                                       OVERRIDE;
+                  PROCEDURE Reset;                                         // Reset File Pointer
+                  FUNCTION  FindNext (VAR DirRec : TTarDirRec) : BOOLEAN;  // Reads next Directory Info Record. FALSE if EOF reached
+                  PROCEDURE ReadFile (Buffer   : POINTER); OVERLOAD;       // Reads file data for last Directory Record
+                  PROCEDURE ReadFile (Stream   : TStream); OVERLOAD;       // -;-
+                  PROCEDURE ReadFile (Filename : STRING);  OVERLOAD;       // -;-
+                  FUNCTION  ReadFile : RawByteString;      OVERLOAD;       // -;-
+
+                  PROCEDURE GetFilePos (VAR Current, Size : INT64);        // Current File Position
+                  PROCEDURE SetFilePos (NewPos : INT64);                   // Set new Current File Position
+                end;
+
+  // --- The TAR Archive Writer CLASS
+  TTarWriter = class
+               protected
+                 FStream      : TStream;
+                 FOwnsStream  : BOOLEAN;
+                 FFinalized   : BOOLEAN;
+                                                   // --- Used at the next "Add" method call: ---
+                 FPermissions : TTarPermissions;   // Access permissions
+                 FUID         : INTEGER;           // User ID
+                 FGID         : INTEGER;           // Group ID
+                 FUserName    : AnsiString;        // User name
+                 FGroupName   : AnsiString;        // Group name
+                 FMode        : TTarModes;         // Mode
+                 FMagic       : AnsiString;        // Contents of the "Magic" field
+                 constructor CreateEmpty;
+               public
+                 constructor Create (TargetStream   : TStream);                            overload;
+                 constructor Create (TargetFilename : string; Mode : integer = fmCreate);  overload;
+                 destructor Destroy; override;                   // Writes End-Of-File Tag
+                 PROCEDURE AddFile   (Filename : STRING;        TarFilename : AnsiString = '');
+                 PROCEDURE AddStream (Stream   : TStream;       TarFilename : AnsiString; FileDateGmt : TDateTime);
+                 PROCEDURE AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime);
+                 PROCEDURE AddDir          (Dirname            : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
+                 PROCEDURE AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
+                 PROCEDURE AddLink         (Filename, Linkname : AnsiString; DateGmt : TDateTime);
+                 PROCEDURE AddVolumeHeader (VolumeId           : AnsiString; DateGmt : TDateTime);
+                 PROCEDURE Finalize;
+                 PROPERTY Permissions : TTarPermissions READ FPermissions WRITE FPermissions;   // Access permissions
+                 PROPERTY UID         : INTEGER         READ FUID         WRITE FUID;           // User ID
+                 PROPERTY GID         : INTEGER         READ FGID         WRITE FGID;           // Group ID
+                 PROPERTY UserName    : AnsiString      READ FUserName    WRITE FUserName;      // User name
+                 PROPERTY GroupName   : AnsiString      READ FGroupName   WRITE FGroupName;     // Group name
+                 PROPERTY Mode        : TTarModes       READ FMode        WRITE FMode;          // Mode
+                 PROPERTY Magic       : AnsiString      READ FMagic       WRITE FMagic;         // Contents of the "Magic" field
+               end;
+
+// --- Some useful constants
+const
+  FILETYPE_NAME : array [TFileType] of string =
+                  ('Regular', 'Link', 'Symbolic Link', 'Char File', 'Block File',
+                   'Directory', 'FIFO File', 'Contiguous', 'Dir Dump', 'Multivol', 'Volume Header');
+
+  ALL_PERMISSIONS     = [tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
+                         tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
+                         tpReadByOther, tpWriteByOther, tpExecuteByOther];
+  READ_PERMISSIONS    = [tpReadByOwner, tpReadByGroup,  tpReadByOther];
+  WRITE_PERMISSIONS   = [tpWriteByOwner, tpWriteByGroup, tpWriteByOther];
+  EXECUTE_PERMISSIONS = [tpExecuteByOwner, tpExecuteByGroup, tpExecuteByOther];
+
+
+function  PermissionString      (Permissions : TTarPermissions) : string;
+function  ConvertFilename       (Filename    : string)          : string;
+function  FileTimeGMT           (FileName    : string)          : TDateTime;  overload;
+function  FileTimeGMT           (SearchRec   : TSearchRec)      : TDateTime;  overload;
+procedure ClearDirRec           (var DirRec  : TTarDirRec);
+
+
+(*
+===============================================================================================
+IMPLEMENTATION
+===============================================================================================
+*)
+
+IMPLEMENTATION
+
+FUNCTION PermissionString (Permissions : TTarPermissions) : STRING;
+BEGIN
+  Result := '';
+  IF tpReadByOwner    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
+  IF tpWriteByOwner   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
+  IF tpExecuteByOwner IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
+  IF tpReadByGroup    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
+  IF tpWriteByGroup   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
+  IF tpExecuteByGroup IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
+  IF tpReadByOther    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
+  IF tpWriteByOther   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
+  IF tpExecuteByOther IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
+END;
+
+
+FUNCTION ConvertFilename  (Filename : STRING) : STRING;
+         // Converts the filename to Unix conventions
+BEGIN
+  (*$IFDEF LINUX *)
+  Result := Filename;
+  (*$ELSE *)
+  Result := StringReplace (Filename, '\', '/', [rfReplaceAll]);
+  (*$ENDIF *)
+END;
+
+
+FUNCTION FileTimeGMT (FileName: STRING): TDateTime;
+         // Returns the Date and Time of the last modification of the given File
+         // The Result is zero if the file could not be found
+         // The Result is given in UTC (GMT) time zone
+VAR
+  SR : TSearchRec;
+BEGIN
+  Result := 0.0;
+  IF FindFirst (FileName, faAnyFile, SR) = 0 THEN
+    Result := FileTimeGMT (SR);
+  FindClose (SR);
+END;
+
+
+FUNCTION FileTimeGMT (SearchRec : TSearchRec) : TDateTime;
+(*$IFDEF MSWINDOWS *)
+VAR
+  SystemFileTime: TSystemTime;
+(*$ENDIF *)
+(*$IFDEF LINUX *)
+VAR
+  TimeVal  : TTimeVal;
+  TimeZone : TTimeZone;
+(*$ENDIF *)
+BEGIN
+  Result := 0.0;
+  (*$IFDEF MSWINDOWS *) (*$WARNINGS OFF *)
+    IF (SearchRec.FindData.dwFileAttributes AND faDirectory) = 0 THEN
+      IF FileTimeToSystemTime (SearchRec.FindData.ftLastWriteTime, SystemFileTime) THEN
+        Result := EncodeDate (SystemFileTime.wYear, SystemFileTime.wMonth, SystemFileTime.wDay)
+                + EncodeTime (SystemFileTime.wHour, SystemFileTime.wMinute, SystemFileTime.wSecond, SystemFileTime.wMilliseconds);
+  (*$ENDIF *) (*$WARNINGS ON *)
+  (*$IFDEF LINUX *)
+     IF SearchRec.Attr AND faDirectory = 0 THEN BEGIN
+       Result := FileDateToDateTime (SearchRec.Time);
+       GetTimeOfDay (TimeVal, TimeZone);
+       Result := Result + TimeZone.tz_minuteswest / (60 * 24);
+       END;
+  (*$ENDIF *)
+end;
+
+
+PROCEDURE ClearDirRec (VAR DirRec : TTarDirRec);
+          // This is included because a FillChar (DirRec, SizeOf (DirRec), 0)
+          // will destroy the long string pointers, leading to strange bugs
+BEGIN
+  WITH DirRec DO BEGIN
+    Name        := '';
+    Size        := 0;
+    DateTime    := 0.0;
+    Permissions := [];
+    FileType    := TFileType (0);
+    LinkName    := '';
+    UID         := 0;
+    GID         := 0;
+    UserName    := '';
+    GroupName   := '';
+    ChecksumOK  := FALSE;
+    Mode        := [];
+    Magic       := '';
+    MajorDevNo  := 0;
+    MinorDevNo  := 0;
+    FilePos     := 0;
+    END;
+END;
+
+
+(*
+===============================================================================================
+TAR format
+===============================================================================================
+*)
+
+CONST
+  RECORDSIZE = 512;
+  NAMSIZ     = 100;
+  TUNMLEN    =  32;
+  TGNMLEN    =  32;
+  CHKBLANKS  = #32#32#32#32#32#32#32#32;
+
+TYPE
+  TTarHeader = PACKED RECORD
+                 Name     : ARRAY [0..NAMSIZ-1] OF AnsiChar;
+                 Mode     : ARRAY [0..7]  OF AnsiChar;
+                 UID      : ARRAY [0..7]  OF AnsiChar;
+                 GID      : ARRAY [0..7]  OF AnsiChar;
+                 Size     : ARRAY [0..11] OF AnsiChar;
+                 MTime    : ARRAY [0..11] OF AnsiChar;
+                 ChkSum   : ARRAY [0..7]  OF AnsiChar;
+                 LinkFlag : AnsiChar;
+                 LinkName : ARRAY [0..NAMSIZ-1] OF AnsiChar;
+                 Magic    : ARRAY [0..7] OF AnsiChar;
+                 UName    : ARRAY [0..TUNMLEN-1] OF AnsiChar;
+                 GName    : ARRAY [0..TGNMLEN-1] OF AnsiChar;
+                 DevMajor : ARRAY [0..7] OF AnsiChar;
+                 DevMinor : ARRAY [0..7] OF AnsiChar;
+               END;
+
+FUNCTION ExtractText (P : PAnsiChar) : AnsiString;
+BEGIN
+  Result := AnsiString (P);
+END;
+
+
+FUNCTION ExtractNumber (P : PAnsiChar) : INTEGER; OVERLOAD;
+VAR
+  Strg : AnsiString;
+BEGIN
+  Strg := AnsiString (Trim (string (P)));
+  P := PAnsiChar (Strg);
+  Result := 0;
+  WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
+    Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
+    INC (P);
+    END;
+END;
+
+
+FUNCTION ExtractNumber64 (P : PAnsiChar) : INT64; OVERLOAD;
+VAR
+  Strg : AnsiString;
+BEGIN
+  Strg := AnsiString (Trim (string (P)));
+  P := PAnsiChar (Strg);
+  Result := 0;
+  WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
+    Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
+    INC (P);
+    END;
+END;
+
+
+
+FUNCTION ExtractNumber (P : PAnsiChar; MaxLen : INTEGER) : INTEGER; OVERLOAD;
+VAR
+  S0   : ARRAY [0..255] OF AnsiChar;
+  Strg : AnsiString;
+BEGIN
+  StrLCopy (S0, P, MaxLen);
+  Strg := AnsiString (Trim (string (S0)));
+  P := PAnsiChar (Strg);
+  Result := 0;
+  WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
+    Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
+    INC (P);
+    END;
+END;
+
+
+FUNCTION ExtractNumber64 (P : PAnsiChar; MaxLen : INTEGER) : INT64; OVERLOAD;
+VAR
+  S0   : ARRAY [0..255] OF AnsiChar;
+  Strg : AnsiString;
+BEGIN
+  StrLCopy (S0, P, MaxLen);
+  Strg := AnsiString (Trim (string (S0)));
+  P := PAnsiChar (Strg);
+  Result := 0;
+  WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
+    Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
+    INC (P);
+    END;
+END;
+
+
+FUNCTION Records (Bytes : INT64) : INT64;
+BEGIN
+  Result := Bytes DIV RECORDSIZE;
+  IF Bytes MOD RECORDSIZE > 0 THEN
+    INC (Result);
+END;
+
+
+PROCEDURE Octal (N : INTEGER; P : PAnsiChar; Len : INTEGER);
+         // Makes a string of octal digits
+         // The string will always be "Len" characters long
+VAR
+  I : INTEGER;
+BEGIN
+  FOR I := Len-2 DOWNTO 0 DO BEGIN
+    (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
+    N := N SHR 3;
+    END;
+  FOR I := 0 TO Len-3 DO
+    IF (P+I)^ = '0'
+      THEN (P+I)^ := #32
+      ELSE BREAK;
+  (P+Len-1)^ := #32;
+END;
+
+
+PROCEDURE Octal64 (N : INT64; P : PAnsiChar; Len : INTEGER);
+         // Makes a string of octal digits
+         // The string will always be "Len" characters long
+VAR
+  I     : INTEGER;
+BEGIN
+  FOR I := Len-2 DOWNTO 0 DO BEGIN
+    (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
+    N := N SHR 3;
+    END;
+  FOR I := 0 TO Len-3 DO
+    IF (P+I)^ = '0'
+      THEN (P+I)^ := #32
+      ELSE BREAK;
+  (P+Len-1)^ := #32;
+END;
+
+
+PROCEDURE OctalN (N : INTEGER; P : PAnsiChar; Len : INTEGER);
+BEGIN
+  Octal (N, P, Len-1);
+  (P+Len-1)^ := #0;
+END;
+
+
+PROCEDURE WriteTarHeader (Dest : TStream; DirRec : TTarDirRec);
+VAR
+  Rec      : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
+  TH       : TTarHeader ABSOLUTE Rec;
+  Mode     : INTEGER;
+  NullDate : TDateTime;
+  Checksum : CARDINAL;
+  I        : INTEGER;
+BEGIN
+  FillChar (Rec, RECORDSIZE, 0);
+  StrLCopy (TH.Name, PAnsiChar (DirRec.Name), NAMSIZ);
+  CASE DirRec.FileType OF
+    ftNormal, ftLink  : Mode := $08000;
+    ftSymbolicLink    : Mode := $0A000;
+    ftDirectory       : Mode := $04000;
+    ELSE                Mode := 0;
+    END;
+  IF tmSaveText IN DirRec.Mode THEN Mode := Mode OR $0200;
+  IF tmSetGid   IN DirRec.Mode THEN Mode := Mode OR $0400;
+  IF tmSetUid   IN DirRec.Mode THEN Mode := Mode OR $0800;
+  IF tpReadByOwner    IN DirRec.Permissions THEN Mode := Mode OR $0100;
+  IF tpWriteByOwner   IN DirRec.Permissions THEN Mode := Mode OR $0080;
+  IF tpExecuteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0040;
+  IF tpReadByGroup    IN DirRec.Permissions THEN Mode := Mode OR $0020;
+  IF tpWriteByGroup   IN DirRec.Permissions THEN Mode := Mode OR $0010;
+  IF tpExecuteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0008;
+  IF tpReadByOther    IN DirRec.Permissions THEN Mode := Mode OR $0004;
+  IF tpWriteByOther   IN DirRec.Permissions THEN Mode := Mode OR $0002;
+  IF tpExecuteByOther IN DirRec.Permissions THEN Mode := Mode OR $0001;
+  OctalN (Mode, @TH.Mode, 8);
+  OctalN (DirRec.UID, @TH.UID, 8);
+  OctalN (DirRec.GID, @TH.GID, 8);
+  Octal64 (DirRec.Size, @TH.Size, 12);
+  NullDate := EncodeDate (1970, 1, 1);
+  IF DirRec.DateTime >= NullDate
+    THEN Octal (Trunc ((DirRec.DateTime - NullDate) * 86400.0), @TH.MTime, 12)
+    ELSE Octal (Trunc (                   NullDate  * 86400.0), @TH.MTime, 12);
+  CASE DirRec.FileType OF
+    ftNormal       : TH.LinkFlag := '0';
+    ftLink         : TH.LinkFlag := '1';
+    ftSymbolicLink : TH.LinkFlag := '2';
+    ftCharacter    : TH.LinkFlag := '3';
+    ftBlock        : TH.LinkFlag := '4';
+    ftDirectory    : TH.LinkFlag := '5';
+    ftFifo         : TH.LinkFlag := '6';
+    ftContiguous   : TH.LinkFlag := '7';
+    ftDumpDir      : TH.LinkFlag := 'D';
+    ftMultiVolume  : TH.LinkFlag := 'M';
+    ftVolumeHeader : TH.LinkFlag := 'V';
+    END;
+  StrLCopy (TH.LinkName, PAnsiChar (DirRec.LinkName), NAMSIZ);
+  StrLCopy (TH.Magic, PAnsiChar (DirRec.Magic + #32#32#32#32#32#32#32#32), 8);
+  StrLCopy (TH.UName, PAnsiChar (DirRec.UserName), TUNMLEN);
+  StrLCopy (TH.GName, PAnsiChar (DirRec.GroupName), TGNMLEN);
+  OctalN (DirRec.MajorDevNo, @TH.DevMajor, 8);
+  OctalN (DirRec.MinorDevNo, @TH.DevMinor, 8);
+  StrMove (TH.ChkSum, CHKBLANKS, 8);
+
+  CheckSum := 0;
+  FOR I := 0 TO SizeOf (TTarHeader)-1 DO
+    INC (CheckSum, INTEGER (ORD (Rec [I])));
+  OctalN (CheckSum, @TH.ChkSum, 8);
+
+  Dest.Write (TH, RECORDSIZE);
+END;
+
+
+(*
+===============================================================================================
+TTarArchive
+===============================================================================================
+*)
+
+CONSTRUCTOR TTarArchive.Create (Stream : TStream);
+BEGIN
+  INHERITED Create;
+  FStream     := Stream;
+  FOwnsStream := FALSE;
+  Reset;
+END;
+
+
+CONSTRUCTOR TTarArchive.Create (Filename : STRING; FileMode : WORD);
+BEGIN
+  INHERITED Create;
+  FStream     := TFileStream.Create (Filename, FileMode);
+  FOwnsStream := TRUE;
+  Reset;
+END;
+
+
+DESTRUCTOR TTarArchive.Destroy;
+BEGIN
+  IF FOwnsStream THEN
+    FStream.Free;
+  INHERITED Destroy;
+END;
+
+
+PROCEDURE TTarArchive.Reset;
+          // Reset File Pointer
+BEGIN
+  FStream.Position := 0;
+  FBytesToGo       := 0;
+END;
+
+
+FUNCTION  TTarArchive.FindNext (VAR DirRec : TTarDirRec) : BOOLEAN;
+          // Reads next Directory Info Record
+          // The Stream pointer must point to the first byte of the tar header
+VAR
+  Rec          : ARRAY [0..RECORDSIZE-1] OF CHAR;
+  CurFilePos   : int64;
+  Header       : TTarHeader ABSOLUTE Rec;
+  I            : INTEGER;
+  HeaderChkSum : WORD;
+  Checksum     : CARDINAL;
+BEGIN
+  // --- Scan until next pointer
+  IF FBytesToGo > 0 THEN
+    FStream.Seek (Records (FBytesToGo) * RECORDSIZE, soCurrent);
+
+  // --- EOF reached?
+  Result := FALSE;
+  CurFilePos := FStream.Position;
+  TRY
+    FStream.ReadBuffer (Rec, RECORDSIZE);
+    if Rec [0] = #0 THEN EXIT;   // EOF reached
+  EXCEPT
+    EXIT;   // EOF reached, too
+    END;
+  Result := TRUE;
+
+  ClearDirRec (DirRec);
+
+  DirRec.FilePos := CurFilePos;
+  DirRec.Name := ExtractText (Header.Name);
+  DirRec.Size := ExtractNumber64 (@Header.Size, 12);
+  DirRec.DateTime := EncodeDate (1970, 1, 1) + (ExtractNumber (@Header.MTime, 12) / 86400.0);
+  I := ExtractNumber (@Header.Mode);
+  IF I AND $0100 <> 0 THEN Include (DirRec.Permissions, tpReadByOwner);
+  IF I AND $0080 <> 0 THEN Include (DirRec.Permissions, tpWriteByOwner);
+  IF I AND $0040 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOwner);
+  IF I AND $0020 <> 0 THEN Include (DirRec.Permissions, tpReadByGroup);
+  IF I AND $0010 <> 0 THEN Include (DirRec.Permissions, tpWriteByGroup);
+  IF I AND $0008 <> 0 THEN Include (DirRec.Permissions, tpExecuteByGroup);
+  IF I AND $0004 <> 0 THEN Include (DirRec.Permissions, tpReadByOther);
+  IF I AND $0002 <> 0 THEN Include (DirRec.Permissions, tpWriteByOther);
+  IF I AND $0001 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOther);
+  IF I AND $0200 <> 0 THEN Include (DirRec.Mode, tmSaveText);
+  IF I AND $0400 <> 0 THEN Include (DirRec.Mode, tmSetGid);
+  IF I AND $0800 <> 0 THEN Include (DirRec.Mode, tmSetUid);
+  CASE Header.LinkFlag OF
+    #0, '0' : DirRec.FileType := ftNormal;
+    '1'     : DirRec.FileType := ftLink;
+    '2'     : DirRec.FileType := ftSymbolicLink;
+    '3'     : DirRec.FileType := ftCharacter;
+    '4'     : DirRec.FileType := ftBlock;
+    '5'     : DirRec.FileType := ftDirectory;
+    '6'     : DirRec.FileType := ftFifo;
+    '7'     : DirRec.FileType := ftContiguous;
+    'D'     : DirRec.FileType := ftDumpDir;
+    'M'     : DirRec.FileType := ftMultiVolume;
+    'V'     : DirRec.FileType := ftVolumeHeader;
+    END;
+  DirRec.LinkName   := ExtractText (Header.LinkName);
+  DirRec.UID        := ExtractNumber (@Header.UID);
+  DirRec.GID        := ExtractNumber (@Header.GID);
+  DirRec.UserName   := ExtractText (Header.UName);
+  DirRec.GroupName  := ExtractText (Header.GName);
+  DirRec.Magic      := AnsiString (Trim (string (Header.Magic)));
+  DirRec.MajorDevNo := ExtractNumber (@Header.DevMajor);
+  DirRec.MinorDevNo := ExtractNumber (@Header.DevMinor);
+
+  HeaderChkSum := ExtractNumber (@Header.ChkSum);   // Calc Checksum
+  CheckSum := 0;
+  StrMove (Header.ChkSum, CHKBLANKS, 8);
+  FOR I := 0 TO SizeOf (TTarHeader)-1 DO
+    INC (CheckSum, INTEGER (ORD (Rec [I])));
+  DirRec.CheckSumOK := WORD (CheckSum) = WORD (HeaderChkSum);
+
+  IF DirRec.FileType in [ftLink, ftSymbolicLink, ftDirectory, ftFifo, ftVolumeHeader]
+    THEN FBytesToGo := 0
+    ELSE FBytesToGo := DirRec.Size;
+END;
+
+
+PROCEDURE TTarArchive.ReadFile (Buffer : POINTER);
+          // Reads file data for the last Directory Record. The entire file is read into the buffer.
+          // The buffer must be large enough to take up the whole file.
+VAR
+  RestBytes : INTEGER;
+BEGIN
+  IF FBytesToGo = 0 THEN EXIT;
+  RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
+  FStream.ReadBuffer (Buffer^, FBytesToGo);
+  FStream.Seek (RestBytes, soCurrent);
+  FBytesToGo := 0;
+END;
+
+
+PROCEDURE TTarArchive.ReadFile (Stream : TStream);
+          // Reads file data for the last Directory Record.
+          // The entire file is written out to the stream.
+          // The stream is left at its current position prior to writing
+VAR
+  RestBytes : INTEGER;
+BEGIN
+  IF FBytesToGo = 0 THEN EXIT;
+  RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
+  Stream.CopyFrom (FStream, FBytesToGo);
+  FStream.Seek (RestBytes, soCurrent);
+  FBytesToGo := 0;
+END;
+
+
+PROCEDURE TTarArchive.ReadFile (Filename : STRING);
+          // Reads file data for the last Directory Record.
+          // The entire file is saved in the given Filename
+VAR
+  FS : TFileStream;
+BEGIN
+  FS := TFileStream.Create (Filename, fmCreate);
+  TRY
+    ReadFile (FS);
+  FINALLY
+    FS.Free;
+    END;
+END;
+
+
+FUNCTION  TTarArchive.ReadFile : RawByteString;
+          // Reads file data for the last Directory Record. The entire file is returned
+          // as a large ANSI string.
+VAR
+  RestBytes : INTEGER;
+BEGIN
+  IF FBytesToGo = 0 THEN EXIT;
+  RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
+  SetLength (Result, FBytesToGo);
+  FStream.ReadBuffer (PAnsiChar (Result)^, FBytesToGo);
+  FStream.Seek (RestBytes, soCurrent);
+  FBytesToGo := 0;
+END;
+
+
+PROCEDURE TTarArchive.GetFilePos (VAR Current, Size : INT64);
+          // Returns the Current Position in the TAR stream
+BEGIN
+  Current := FStream.Position;
+  Size    := FStream.Size;
+END;
+
+
+PROCEDURE TTarArchive.SetFilePos (NewPos : INT64);                   // Set new Current File Position
+BEGIN
+  IF NewPos < FStream.Size THEN
+    FStream.Seek (NewPos, soBeginning);
+END;
+
+
+(*
+===============================================================================================
+TTarWriter
+===============================================================================================
+*)
+
+CONSTRUCTOR TTarWriter.CreateEmpty;
+VAR
+  TP : TTarPermission;
+BEGIN
+  INHERITED Create;
+  FOwnsStream  := FALSE;
+  FFinalized   := FALSE;
+  FPermissions := [];
+  FOR TP := Low (TP) TO High (TP) DO
+    Include (FPermissions, TP);
+  FUID       := 0;
+  FGID       := 0;
+  FUserName  := '';
+  FGroupName := '';
+  FMode      := [];
+  FMagic     := 'ustar';
+END;
+
+CONSTRUCTOR TTarWriter.Create (TargetStream   : TStream);
+BEGIN
+  CreateEmpty;
+  FStream     := TargetStream;
+  FOwnsStream := FALSE;
+END;
+
+
+CONSTRUCTOR TTarWriter.Create (TargetFilename : STRING; Mode : INTEGER = fmCreate);
+BEGIN
+  CreateEmpty;
+  FStream     := TFileStream.Create (TargetFilename, Mode);
+  FOwnsStream := TRUE;
+END;
+
+
+DESTRUCTOR TTarWriter.Destroy;
+BEGIN
+  IF NOT FFinalized THEN BEGIN
+    Finalize;
+    FFinalized := TRUE;
+    END;
+  IF FOwnsStream THEN
+    FStream.Free;
+  INHERITED Destroy;
+END;
+
+
+PROCEDURE TTarWriter.AddFile   (Filename : STRING;  TarFilename : AnsiString = '');
+VAR
+  S    : TFileStream;
+  Date : TDateTime;
+BEGIN
+  Date := FileTimeGMT (Filename);
+  IF TarFilename = ''
+    THEN TarFilename := AnsiString (ConvertFilename (Filename))
+    ELSE TarFilename := AnsiString (ConvertFilename (string (TarFilename)));
+  S := TFileStream.Create (Filename, fmOpenRead OR fmShareDenyWrite);
+  TRY
+    AddStream (S, TarFilename, Date);
+  FINALLY
+    S.Free
+    END;
+END;
+
+
+PROCEDURE TTarWriter.AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime);
+VAR
+  DirRec      : TTarDirRec;
+  Rec         : ARRAY [0..RECORDSIZE-1] OF CHAR;
+  BytesToRead : INT64;      // Bytes to read from the Source Stream
+  BlockSize   : INT64;      // Bytes to write out for the current record
+BEGIN
+  ClearDirRec (DirRec);
+  DirRec.Name        := TarFilename;
+  DirRec.Size        := Stream.Size - Stream.Position;
+  DirRec.DateTime    := FileDateGmt;
+  DirRec.Permissions := FPermissions;
+  DirRec.FileType    := ftNormal;
+  DirRec.LinkName    := '';
+  DirRec.UID         := FUID;
+  DirRec.GID         := FGID;
+  DirRec.UserName    := FUserName;
+  DirRec.GroupName   := FGroupName;
+  DirRec.ChecksumOK  := TRUE;
+  DirRec.Mode        := FMode;
+  DirRec.Magic       := FMagic;
+  DirRec.MajorDevNo  := 0;
+  DirRec.MinorDevNo  := 0;
+
+  WriteTarHeader (FStream, DirRec);
+  BytesToRead := DirRec.Size;
+  WHILE BytesToRead > 0 DO BEGIN
+    BlockSize := BytesToRead;
+    IF BlockSize > RECORDSIZE THEN BlockSize := RECORDSIZE;
+    FillChar (Rec, RECORDSIZE, 0);
+    Stream.Read (Rec, BlockSize);
+    FStream.Write (Rec, RECORDSIZE);
+    DEC (BytesToRead, BlockSize);
+    END;
+END;
+
+
+PROCEDURE TTarWriter.AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime);
+VAR
+  S : TStringStream;
+BEGIN
+  S := TStringStream.Create (Contents);
+  TRY
+    AddStream (S, TarFilename, FileDateGmt);
+  FINALLY
+    S.Free
+    END
+END;
+
+
+PROCEDURE TTarWriter.AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
+VAR
+  DirRec      : TTarDirRec;
+BEGIN
+  ClearDirRec (DirRec);
+  DirRec.Name        := Dirname;
+  DirRec.Size        := MaxDirSize;
+  DirRec.DateTime    := DateGmt;
+  DirRec.Permissions := FPermissions;
+  DirRec.FileType    := ftDirectory;
+  DirRec.LinkName    := '';
+  DirRec.UID         := FUID;
+  DirRec.GID         := FGID;
+  DirRec.UserName    := FUserName;
+  DirRec.GroupName   := FGroupName;
+  DirRec.ChecksumOK  := TRUE;
+  DirRec.Mode        := FMode;
+  DirRec.Magic       := FMagic;
+  DirRec.MajorDevNo  := 0;
+  DirRec.MinorDevNo  := 0;
+
+  WriteTarHeader (FStream, DirRec);
+END;
+
+
+PROCEDURE TTarWriter.AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
+VAR
+  DirRec : TTarDirRec;
+BEGIN
+  ClearDirRec (DirRec);
+  DirRec.Name        := Filename;
+  DirRec.Size        := 0;
+  DirRec.DateTime    := DateGmt;
+  DirRec.Permissions := FPermissions;
+  DirRec.FileType    := ftSymbolicLink;
+  DirRec.LinkName    := Linkname;
+  DirRec.UID         := FUID;
+  DirRec.GID         := FGID;
+  DirRec.UserName    := FUserName;
+  DirRec.GroupName   := FGroupName;
+  DirRec.ChecksumOK  := TRUE;
+  DirRec.Mode        := FMode;
+  DirRec.Magic       := FMagic;
+  DirRec.MajorDevNo  := 0;
+  DirRec.MinorDevNo  := 0;
+
+  WriteTarHeader (FStream, DirRec);
+END;
+
+
+PROCEDURE TTarWriter.AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
+VAR
+  DirRec : TTarDirRec;
+BEGIN
+  ClearDirRec (DirRec);
+  DirRec.Name        := Filename;
+  DirRec.Size        := 0;
+  DirRec.DateTime    := DateGmt;
+  DirRec.Permissions := FPermissions;
+  DirRec.FileType    := ftLink;
+  DirRec.LinkName    := Linkname;
+  DirRec.UID         := FUID;
+  DirRec.GID         := FGID;
+  DirRec.UserName    := FUserName;
+  DirRec.GroupName   := FGroupName;
+  DirRec.ChecksumOK  := TRUE;
+  DirRec.Mode        := FMode;
+  DirRec.Magic       := FMagic;
+  DirRec.MajorDevNo  := 0;
+  DirRec.MinorDevNo  := 0;
+
+  WriteTarHeader (FStream, DirRec);
+END;
+
+
+PROCEDURE TTarWriter.AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime);
+VAR
+  DirRec : TTarDirRec;
+BEGIN
+  ClearDirRec (DirRec);
+  DirRec.Name        := VolumeId;
+  DirRec.Size        := 0;
+  DirRec.DateTime    := DateGmt;
+  DirRec.Permissions := FPermissions;
+  DirRec.FileType    := ftVolumeHeader;
+  DirRec.LinkName    := '';
+  DirRec.UID         := FUID;
+  DirRec.GID         := FGID;
+  DirRec.UserName    := FUserName;
+  DirRec.GroupName   := FGroupName;
+  DirRec.ChecksumOK  := TRUE;
+  DirRec.Mode        := FMode;
+  DirRec.Magic       := FMagic;
+  DirRec.MajorDevNo  := 0;
+  DirRec.MinorDevNo  := 0;
+
+  WriteTarHeader (FStream, DirRec);
+END;
+
+
+PROCEDURE TTarWriter.Finalize;
+          // Writes the End-Of-File Tag
+          // Data after this tag will be ignored
+          // The destructor calls this automatically if you didn't do it before
+VAR
+  Rec : ARRAY [0..RECORDSIZE-1] OF CHAR;
+BEGIN
+  FillChar (Rec, SizeOf (Rec), 0);
+  FStream.Write (Rec, RECORDSIZE);
+  FFinalized := TRUE;
+END;
+
+
+end.
+
diff --git a/dcm2laz/dcm2.ico b/dcm2laz/dcm2.ico
new file mode 100644
index 0000000..375ec28
Binary files /dev/null and b/dcm2laz/dcm2.ico differ
diff --git a/dcm2laz/dcm2.lpi b/dcm2laz/dcm2.lpi
new file mode 100644
index 0000000..e840254
--- /dev/null
+++ b/dcm2laz/dcm2.lpi
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="9"/>
+    <General>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="dcm2"/>
+      <ResourceType Value="res"/>
+      <UseXPManifest Value="True"/>
+      <Icon Value="0"/>
+    </General>
+    <i18n>
+      <EnableI18N LFM="False"/>
+    </i18n>
+    <VersionInfo>
+      <StringTable ProductVersion=""/>
+    </VersionInfo>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
+      <ExcludeFileFilter Value="*.(bak|ppu|o|so);*~;backup"/>
+    </PublishOptions>
+    <RunParams>
+      <local>
+        <FormatVersion Value="1"/>
+      </local>
+    </RunParams>
+    <RequiredPackages Count="1">
+      <Item1>
+        <PackageName Value="LCL"/>
+      </Item1>
+    </RequiredPackages>
+    <Units Count="2">
+      <Unit0>
+        <Filename Value="dcm2.lpr"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="dcm2"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="form.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="Form1"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+        <UnitName Value="form"/>
+      </Unit1>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="dcm2"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <GenerateDebugInfo Value="False"/>
+      </Debugging>
+      <LinkSmart Value="True"/>
+      <Options>
+        <Win32>
+          <GraphicApplication Value="True"/>
+        </Win32>
+      </Options>
+    </Linking>
+    <Other>
+      <CompilerMessages>
+        <MsgFileName Value=""/>
+      </CompilerMessages>
+      <CompilerPath Value="$(CompPath)"/>
+    </Other>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>
diff --git a/dcm2laz/dcm2.lpr b/dcm2laz/dcm2.lpr
new file mode 100644
index 0000000..0fd0900
--- /dev/null
+++ b/dcm2laz/dcm2.lpr
@@ -0,0 +1,21 @@
+program dcm2;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}{$IFDEF UseCThreads}
+  cthreads,
+  {$ENDIF}{$ENDIF}
+  Interfaces, // this includes the LCL widgetset
+  Forms, form
+  { you can add units after this };
+
+{$R *.res}
+
+begin
+  RequireDerivedFormResource := True;
+  Application.Initialize;
+  Application.CreateForm(TForm1, Form1);
+  Application.Run;
+end.
+
diff --git a/dcm2laz/dcm2.lps b/dcm2laz/dcm2.lps
new file mode 100644
index 0000000..7ec5e85
--- /dev/null
+++ b/dcm2laz/dcm2.lps
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectSession>
+    <Version Value="9"/>
+    <BuildModes Active="Default"/>
+    <Units Count="6">
+      <Unit0>
+        <Filename Value="dcm2.lpr"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="dcm2"/>
+        <UsageCount Value="38"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="form.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="Form1"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+        <UnitName Value="form"/>
+        <IsVisibleTab Value="True"/>
+        <EditorIndex Value="0"/>
+        <WindowIndex Value="0"/>
+        <TopLine Value="61"/>
+        <CursorPos X="34" Y="83"/>
+        <UsageCount Value="38"/>
+        <Loaded Value="True"/>
+        <LoadedDesigner Value="True"/>
+      </Unit1>
+      <Unit2>
+        <Filename Value="../../../lazarus/fpc/2.6.0/source/packages/fcl-registry/src/registry.pp"/>
+        <UnitName Value="registry"/>
+        <WindowIndex Value="0"/>
+        <TopLine Value="427"/>
+        <CursorPos X="40" Y="441"/>
+        <UsageCount Value="16"/>
+      </Unit2>
+      <Unit3>
+        <Filename Value="../../../lazarus/components/lazutils/fileutil.pas"/>
+        <UnitName Value="FileUtil"/>
+        <EditorIndex Value="3"/>
+        <WindowIndex Value="0"/>
+        <TopLine Value="107"/>
+        <CursorPos X="70" Y="116"/>
+        <UsageCount Value="14"/>
+        <Loaded Value="True"/>
+      </Unit3>
+      <Unit4>
+        <Filename Value="LibTar.pas"/>
+        <UnitName Value="LibTar"/>
+        <EditorIndex Value="2"/>
+        <WindowIndex Value="0"/>
+        <TopLine Value="136"/>
+        <CursorPos X="18" Y="75"/>
+        <UsageCount Value="13"/>
+        <Loaded Value="True"/>
+      </Unit4>
+      <Unit5>
+        <Filename Value="untar.pas"/>
+        <UnitName Value="untar"/>
+        <EditorIndex Value="1"/>
+        <WindowIndex Value="0"/>
+        <TopLine Value="92"/>
+        <CursorPos X="26" Y="157"/>
+        <UsageCount Value="13"/>
+        <Loaded Value="True"/>
+      </Unit5>
+    </Units>
+    <General>
+      <ActiveWindowIndexAtStart Value="0"/>
+    </General>
+    <JumpHistory Count="30" HistoryIndex="29">
+      <Position1>
+        <Filename Value="untar.pas"/>
+        <Caret Line="187" Column="28" TopLine="177"/>
+      </Position1>
+      <Position2>
+        <Filename Value="untar.pas"/>
+        <Caret Line="189" Column="18" TopLine="176"/>
+      </Position2>
+      <Position3>
+        <Filename Value="untar.pas"/>
+        <Caret Line="5" Column="70" TopLine="1"/>
+      </Position3>
+      <Position4>
+        <Filename Value="untar.pas"/>
+        <Caret Line="25" Column="73" TopLine="16"/>
+      </Position4>
+      <Position5>
+        <Filename Value="untar.pas"/>
+        <Caret Line="32" Column="31" TopLine="16"/>
+      </Position5>
+      <Position6>
+        <Filename Value="untar.pas"/>
+        <Caret Line="48" Column="28" TopLine="25"/>
+      </Position6>
+      <Position7>
+        <Filename Value="untar.pas"/>
+        <Caret Line="78" Column="39" TopLine="68"/>
+      </Position7>
+      <Position8>
+        <Filename Value="untar.pas"/>
+        <Caret Line="24" Column="5" TopLine="7"/>
+      </Position8>
+      <Position9>
+        <Filename Value="form.pas"/>
+        <Caret Line="62" Column="15" TopLine="44"/>
+      </Position9>
+      <Position10>
+        <Filename Value="form.pas"/>
+        <Caret Line="67" Column="50" TopLine="47"/>
+      </Position10>
+      <Position11>
+        <Filename Value="form.pas"/>
+        <Caret Line="70" Column="29" TopLine="47"/>
+      </Position11>
+      <Position12>
+        <Filename Value="form.pas"/>
+        <Caret Line="69" Column="37" TopLine="56"/>
+      </Position12>
+      <Position13>
+        <Filename Value="form.pas"/>
+        <Caret Line="72" Column="27" TopLine="56"/>
+      </Position13>
+      <Position14>
+        <Filename Value="form.pas"/>
+        <Caret Line="186" Column="16" TopLine="158"/>
+      </Position14>
+      <Position15>
+        <Filename Value="form.pas"/>
+        <Caret Line="4" Column="99" TopLine="1"/>
+      </Position15>
+      <Position16>
+        <Filename Value="form.pas"/>
+        <Caret Line="47" Column="24" TopLine="20"/>
+      </Position16>
+      <Position17>
+        <Filename Value="form.pas"/>
+        <Caret Line="72" Column="27" TopLine="44"/>
+      </Position17>
+      <Position18>
+        <Filename Value="form.pas"/>
+        <Caret Line="65" Column="1" TopLine="59"/>
+      </Position18>
+      <Position19>
+        <Filename Value="form.pas"/>
+        <Caret Line="73" Column="27" TopLine="59"/>
+      </Position19>
+      <Position20>
+        <Filename Value="form.pas"/>
+        <Caret Line="149" Column="16" TopLine="121"/>
+      </Position20>
+      <Position21>
+        <Filename Value="form.pas"/>
+        <Caret Line="187" Column="16" TopLine="159"/>
+      </Position21>
+      <Position22>
+        <Filename Value="form.pas"/>
+        <Caret Line="4" Column="111" TopLine="1"/>
+      </Position22>
+      <Position23>
+        <Filename Value="form.pas"/>
+        <Caret Line="47" Column="24" TopLine="29"/>
+      </Position23>
+      <Position24>
+        <Filename Value="form.pas"/>
+        <Caret Line="76" Column="6" TopLine="60"/>
+      </Position24>
+      <Position25>
+        <Filename Value="form.pas"/>
+        <Caret Line="16" Column="37" TopLine="1"/>
+      </Position25>
+      <Position26>
+        <Filename Value="form.pas"/>
+        <Caret Line="47" Column="24" TopLine="20"/>
+      </Position26>
+      <Position27>
+        <Filename Value="form.pas"/>
+        <Caret Line="84" Column="24" TopLine="66"/>
+      </Position27>
+      <Position28>
+        <Filename Value="form.pas"/>
+        <Caret Line="147" Column="16" TopLine="119"/>
+      </Position28>
+      <Position29>
+        <Filename Value="form.pas"/>
+        <Caret Line="185" Column="16" TopLine="157"/>
+      </Position29>
+      <Position30>
+        <Filename Value="form.pas"/>
+        <Caret Line="283" Column="19" TopLine="265"/>
+      </Position30>
+    </JumpHistory>
+  </ProjectSession>
+  <EditorMacros Count="0"/>
+</CONFIG>
diff --git a/dcm2laz/dcm2.res b/dcm2laz/dcm2.res
new file mode 100644
index 0000000..def481f
Binary files /dev/null and b/dcm2laz/dcm2.res differ
diff --git a/dcm2laz/dcm2niigui.ico b/dcm2laz/dcm2niigui.ico
new file mode 100644
index 0000000..375ec28
Binary files /dev/null and b/dcm2laz/dcm2niigui.ico differ
diff --git a/dcm2laz/form.lfm b/dcm2laz/form.lfm
new file mode 100644
index 0000000..da2db77
--- /dev/null
+++ b/dcm2laz/form.lfm
@@ -0,0 +1,134 @@
+object Form1: TForm1
+  Left = 126
+  Height = 480
+  Top = 178
+  Width = 760
+  AllowDropFiles = True
+  Caption = 'dcm2'
+  ClientHeight = 461
+  ClientWidth = 760
+  Constraints.MinHeight = 120
+  Constraints.MinWidth = 640
+  Menu = MainMenu1
+  OnClose = FormClose
+  OnCreate = FormCreate
+  OnDropFiles = FormDropFiles
+  OnResize = FormResize
+  Position = poScreenCenter
+  LCLVersion = '1.2.4.0'
+  Visible = True
+  object Panel1: TPanel
+    Left = 0
+    Height = 34
+    Top = 0
+    Width = 760
+    Align = alTop
+    BevelOuter = bvNone
+    ClientHeight = 34
+    ClientWidth = 760
+    TabOrder = 0
+    object compressCheck: TCheckBox
+      Left = 80
+      Height = 17
+      Top = 9
+      Width = 20
+      Checked = True
+      OnClick = compressCheckClick
+      ParentBidiMode = False
+      State = cbChecked
+      TabOrder = 0
+    end
+    object outnameLabel: TLabel
+      Left = 104
+      Height = 13
+      Top = 9
+      Width = 64
+      Caption = 'Output Name'
+      ParentColor = False
+      ParentShowHint = False
+      ShowHint = True
+    end
+    object outnameEdit: TEdit
+      Left = 192
+      Height = 21
+      Hint = 'Name for NIfTI images. Special characters are %f (Folder name) %i (ID) %n (patient Name) %p (Protocol name) %s (Series number) %t (Time)'
+      Top = 8
+      Width = 232
+      OnKeyUp = outnameEditKeyUp
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 1
+      Text = 'outnameEdit'
+    end
+    object Label2: TLabel
+      Left = 8
+      Height = 13
+      Hint = 'Set whether NIfTI images are compressed (.nii.gz) or not (.nii)'
+      Top = 9
+      Width = 47
+      Caption = 'Compress'
+      ParentColor = False
+    end
+    object outputFolderLabel: TLabel
+      Left = 432
+      Height = 13
+      Top = 10
+      Width = 65
+      Caption = 'Output folder'
+      ParentColor = False
+    end
+    object outputFolderName: TButton
+      Left = 520
+      Height = 25
+      Hint = 'NIfTI files will be saved to this folder. Press this button and click Cancel if you want files NIfTI images saved to same folder as DICOM input'
+      Top = 5
+      Width = 240
+      Caption = 'input folder'
+      OnClick = outputFolderNameClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 2
+    end
+  end
+  object Memo1: TMemo
+    Left = 0
+    Height = 427
+    Top = 34
+    Width = 760
+    Align = alClient
+    ScrollBars = ssAutoBoth
+    TabOrder = 1
+  end
+  object MainMenu1: TMainMenu
+    left = 24
+    top = 48
+    object FileMenu: TMenuItem
+      Caption = 'File'
+      object DicomMenu: TMenuItem
+        Caption = 'DICOM to NIfTI...'
+        OnClick = DicomMenuClick
+      end
+      object ParRecMenu: TMenuItem
+        Caption = 'PAR/REC to NIfTI...'
+        OnClick = ParRecMenuClick
+      end
+      object ResetMenu: TMenuItem
+        Caption = 'Reset defaults'
+        OnClick = ResetMenuClick
+      end
+    end
+    object EditMenu: TMenuItem
+      Caption = 'Edit'
+      object CopyMenu: TMenuItem
+        Caption = 'Copy'
+        OnClick = CopyMenuClick
+      end
+    end
+  end
+  object OpenDialog1: TOpenDialog
+    Filter = 'Philips research (*.par)|*.PAR;*.par'
+    Options = [ofAllowMultiSelect, ofEnableSizing, ofViewDetail]
+    left = 96
+    top = 48
+  end
+end
diff --git a/dcm2laz/form.pas b/dcm2laz/form.pas
new file mode 100644
index 0000000..a5a80eb
--- /dev/null
+++ b/dcm2laz/form.pas
@@ -0,0 +1,367 @@
+unit form;
+
+ {$IFDEF FPC} {$mode delphi}{$H+} {$ENDIF}
+interface
+
+uses
+  {$IFNDEF UNIX} Registry, {$ENDIF}
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
+  StdCtrls, Menus, Process, untar;
+
+type
+
+  { TForm1 }
+  TForm1 = class(TForm)
+    MainMenu1: TMainMenu;
+    FileMenu: TMenuItem;
+    EditMenu: TMenuItem;
+    CopyMenu: TMenuItem;
+    DicomMenu: TMenuItem;
+    ResetMenu: TMenuItem;
+    OpenDialog1: TOpenDialog;
+    ParRecMenu: TMenuItem;
+    outputFolderName: TButton;
+    compressCheck: TCheckBox;
+    Label2: TLabel;
+    outputFolderLabel: TLabel;
+    outnameEdit: TEdit;
+    outnameLabel: TLabel;
+    Memo1: TMemo;
+    Panel1: TPanel;
+    procedure compressCheckClick(Sender: TObject);
+    procedure DicomMenuClick(Sender: TObject);
+    procedure FormResize(Sender: TObject);
+    function getOutputFolder: string;
+
+    procedure outnameEditKeyUp(Sender: TObject; var Key: Word;
+      Shift: TShiftState);
+    procedure ParRecMenuClick(Sender: TObject);
+    procedure ProcessFile(infilename: string);
+    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDropFiles(Sender: TObject; const FileNames: array of String);
+    procedure CopyMenuClick(Sender: TObject);
+    procedure outputFolderNameClick(Sender: TObject);
+    procedure ResetMenuClick(Sender: TObject);
+    procedure RunCmd (lCmd: string);
+    function getExeName : string; //return path for command line tool
+    procedure readIni (ForceReset: boolean); //load preferences
+    procedure writeIni; //save preferences
+  private
+    { private declarations }
+  public
+    { public declarations }
+  end;
+
+var
+  Form1: TForm1;
+
+implementation
+{$R *.lfm}
+
+const kExeName = 'dcm2niix' ;
+  var
+    isAppDoneInitializing : boolean = false;
+
+function FindDefaultExecutablePathX(const Executable: string): string;
+begin
+     result := FindDefaultExecutablePath(kExeName);
+     if result = '' then
+        result := FindDefaultExecutablePath(ExtractFilePath  (paramstr(0)) +kExeName);
+end;
+
+function TForm1.getExeName : string;
+var
+  lF: string;
+begin
+     result := FindDefaultExecutablePathX(kExeName);
+     if not fileexists(result) then begin
+        lF :=  ExtractFilePath (paramstr(0));
+        result := lF+kExeName;
+        if not fileexists(result) then begin
+           Memo1.Lines.Clear;
+           memo1.Lines.Add('Error: unable to find executable '+kExeName+' in path');
+           memo1.Lines.Add(' Solution: copy '+kExeName+' to '+lF);
+           result := '';
+        end;  //not in same folder as GUI
+     end; //not in path
+     {$IFNDEF UNIX} //strip .exe for Windows
+     result := ChangeFileExt(result, '');
+     {$ENDIF}
+end; //exeName()
+
+{$IFDEF UNIX}
+function iniName : string;
+begin
+     result := GetEnvironmentVariable ('HOME')+PathDelim+'.dcm2nii.ini';
+end;
+
+procedure TForm1.writeIni;
+var
+   iniFile : TextFile;
+ begin
+   AssignFile(iniFile, iniName);
+   ReWrite(iniFile);
+   if (compressCheck.checked) then
+      WriteLn(iniFile, 'isGZ=1')
+   else
+       WriteLn(iniFile, 'isGZ=0');
+   WriteLn(iniFile, 'filename='+outnameEdit.caption);
+   CloseFile(iniFile);
+end; //writeIni
+
+procedure TForm1.readIni (ForceReset: boolean);
+var
+  fileData, rowData : TStringList;
+  row, i: integer;
+  opts_isGz: boolean;
+  opts_filename: string;
+begin
+     opts_isGz := true;
+     //opts_outdir := '';
+     opts_filename := '%t_%p_%s';
+     if FileExists( iniName) and (not (ForceReset )) then begin
+        fileData := TStringList.Create;
+        fileData.LoadFromFile(iniName);  // Load from Testing.txt file
+        if (fileData.Count > 0) then begin
+           rowData := TStringList.Create;
+           rowData.Delimiter := '=';
+           for row := 0 to (fileData.Count-1) do begin //for each row of file
+               rowData.DelimitedText:=fileData[row];
+               if ((rowData.Count > 1) and (CompareText(rowData[0] ,'isGZ')= 0)) then
+                  opts_isGz := (CompareText(rowData[1],'1') = 0);
+               if ((rowData.Count > 1) and (CompareText(rowData[0] ,'filename')= 0)) then begin
+                  opts_filename := '';
+                  if (rowData.Count > 2) then
+                     for i := 1 to (rowData.Count-2) do
+                         opts_filename := opts_filename+ rowData[i]+' ';
+                  opts_filename := opts_filename+ rowData[rowData.Count-1];
+               end;
+           end;
+          rowData.Free;
+        end;
+        fileData.Free;
+     end else
+         memo1.Lines.Add('Using default settings');
+     compressCheck.Checked := opts_isGz;
+     outnameEdit.Caption := opts_filename;
+     getExeName;
+end; //readIni()
+{$ELSE}
+//For Windows we save preferences in the registry to ensure user has write access
+procedure TForm1.writeIni;
+var
+  ARegistry: TRegistry;
+begin
+     ARegistry := TRegistry.Create;
+     ARegistry.RootKey := HKEY_CURRENT_USER;//HKEY_LOCAL_MACHINE;
+     if ARegistry.OpenKey ('\Software\dcm2nii',true) then begin
+       	  ARegistry.WriteBool('isGZ', compressCheck.Checked );
+       	  ARegistry.WriteString('filename', outnameEdit.Caption );
+     end;
+     ARegistry.Free;
+end; //writeIni()
+
+procedure TForm1.readIni (ForceReset: boolean);
+var
+  ARegistry: TRegistry;
+  opts_isGz: boolean;
+  opts_filename: string;
+begin
+     //showmessage(inttostr(SizeOf(Integer)));
+     opts_isGz := true;
+     opts_filename := '%t_%p_%s';
+     if not ForceReset then begin
+       ARegistry := TRegistry.Create;
+       ARegistry.RootKey := HKEY_CURRENT_USER;//HKEY_LOCAL_MACHINE;
+       if ARegistry.OpenKey ('\Software\dcm2nii',true) then begin
+       	    if ARegistry.ValueExists( 'isGZ' ) then
+          	   opts_isGz := ARegistry.ReadBool( 'isGZ' );
+       	    if ARegistry.ValueExists( 'isGZ' ) then
+          	   opts_filename := ARegistry.ReadString( 'filename' );
+       end;
+       ARegistry.Free;
+     end;
+     compressCheck.Checked := opts_isGz;
+     outnameEdit.Caption := opts_filename;
+     getExeName;
+end; //readIni()
+{$ENDIF}
+
+procedure TForm1.RunCmd (lCmd: string);
+//http://wiki.freepascal.org/Executing_External_Programs
+var
+  OutputLines: TStringList;
+  MemStream: TMemoryStream;
+  OurProcess: TProcess;
+  NumBytes: LongInt;
+  BytesRead: LongInt;
+const
+  READ_BYTES = 2048;
+begin
+   if (not isAppDoneInitializing) then exit;
+   if (getExeName = '') then exit;
+   Memo1.Lines.Clear;
+   Memo1.Lines.Add(lCmd);
+   Form1.refresh; Memo1.refresh; Memo1.invalidate;
+   MemStream := TMemoryStream.Create;
+   BytesRead := 0;
+   OurProcess := TProcess.Create(nil);
+   {$IFDEF UNIX}
+   OurProcess.Environment.Add(GetEnvironmentVariable('PATH'));
+   {$ENDIF}
+   OurProcess.CommandLine := lCmd;
+  // We cannot use poWaitOnExit here since we don't
+  // know the size of the output. On Linux the size of the
+  // output pipe is 2 kB; if the output data is more, we
+  // need to read the data. This isn't possible since we are
+  // waiting. So we get a deadlock here if we use poWaitOnExit.
+  OurProcess.Options := [poUsePipes, poNoConsole];
+  OurProcess.Execute;
+  while True do begin
+    // make sure we have room
+    MemStream.SetSize(BytesRead + READ_BYTES);
+    // try reading it
+    NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
+    if NumBytes > 0 // All read() calls will block, except the final one.
+    then begin
+      Inc(BytesRead, NumBytes);
+    end else
+      BREAK // Program has finished execution.
+  end;
+  MemStream.SetSize(BytesRead);
+  OutputLines := TStringList.Create;
+  OutputLines.LoadFromStream(MemStream);
+  Memo1.Lines.AddStrings(OutputLines);
+  OutputLines.Free;
+  OurProcess.Free;
+  MemStream.Free;
+end;
+
+function TForm1.getOutputFolder: string;
+begin
+     if (outputFolderName.Tag > 0) then
+        result := outputFolderName.Caption
+     else
+         result := '';
+end; //getOutputFolder
+
+procedure TForm1.ProcessFile(infilename: string);
+var
+  cmd, outputFolder, inFolder: string;
+begin
+  inFolder := infilename;
+  if isTGZ(inFolder) then begin
+  	 infolder := deTGZ(infolder);
+     if infolder = '' then exit; //error
+ end;
+ cmd := getExeName +' ';
+ if compressCheck.checked then
+    cmd := cmd + '-z y '
+ else
+     cmd := cmd + '-z n ';
+ outputFolder := getOutputFolder;
+ if length(outputFolder) > 0 then
+     cmd := cmd + '-o '+outputFolder+' ';
+ cmd := cmd + '-f "'+outnameEdit.Text+'" ';
+ if length(inFolder) > 0 then
+     cmd := cmd +'"'+inFolder+'"';
+ RunCmd(cmd);
+end; //ProcessFile()
+
+procedure TForm1.outnameEditKeyUp(Sender: TObject; var Key: Word;
+  Shift: TShiftState);
+begin
+      ProcessFile('');
+end; //outnameEditKeyUp()
+
+procedure TForm1.ParRecMenuClick(Sender: TObject);
+var
+  lI: integer;
+begin
+  if not OpenDialog1.execute then exit;
+  //ProcessFile(OpenDialog1.filename);
+  if OpenDialog1.Files.count < 1 then exit;
+     for lI := 0 to (OpenDialog1.Files.count-1) do
+         ProcessFile(OpenDialog1.Files[lI]);
+end; //ParRecMenuClick()
+
+function getDirPrompt (lDefault: string): string;
+begin
+  result := lDefault;  // Set the starting directory
+  chdir(result); //start search from default dir...
+  if SelectDirectory(result, [sdAllowCreate,sdPerformCreate,sdPrompt], 0) then
+     chdir(result)
+  else
+      result := '';
+end;  //getDirPrompt()
+
+procedure TForm1.DicomMenuClick(Sender: TObject);
+var
+  dir: string;
+begin
+     dir := getDirPrompt('');
+     ProcessFile( dir);
+end; //DicomMenuClick()
+
+procedure TForm1.compressCheckClick(Sender: TObject);
+begin
+  ProcessFile('');
+end;
+
+procedure TForm1.FormResize(Sender: TObject);
+begin
+  outputFolderName.width := Form1.Width-outputFolderName.left-2;
+end; //FormResize()
+
+procedure TForm1.FormDropFiles(Sender: TObject; const FileNames: array of String);
+begin
+    ProcessFile( FileNames[0]);
+end; //FormDropFiles()
+
+procedure TForm1.CopyMenuClick(Sender: TObject);
+begin
+     Memo1.SelectAll;
+     Memo1.CopyToClipboard;
+end; //CopyMenuClick()
+
+procedure TForm1.outputFolderNameClick(Sender: TObject);
+var
+  lDir : string;
+begin
+     if (outputFolderName.Tag > 0) then //start search from prior location
+        lDir := outputFolderName.Caption
+     else
+         lDir := '';
+     lDir := getDirPrompt(lDir);
+     outputFolderName.Tag := length(lDir);
+     if length(lDir) > 0 then
+        outputFolderName.Caption := lDir
+     else
+         outputFolderName.Caption := 'input folder';
+end; //outputFolderNameClick()
+
+procedure TForm1.ResetMenuClick(Sender: TObject);
+begin
+  isAppDoneInitializing := false;
+     readIni(true);
+     isAppDoneInitializing := true;
+     ProcessFile('');
+end;
+
+procedure TForm1.FormCreate(Sender: TObject);
+begin
+     readIni(false);
+     //memo1.lines.add('for details see http://www.mccauslandcenter.sc.edu/CRNL/tools/dcm2niix');
+     application.ShowButtonGlyphs:= sbgNever;
+     isAppDoneInitializing := true;
+     ProcessFile('');
+end; //FormCreate()
+
+procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
+begin
+     writeIni;
+end; //FormClose()
+
+end.
+
diff --git a/dcm2laz/untar.pas b/dcm2laz/untar.pas
new file mode 100644
index 0000000..93a2ab7
--- /dev/null
+++ b/dcm2laz/untar.pas
@@ -0,0 +1,168 @@
+unit untar;
+
+interface
+{$IFDEF FPC}{$mode delphi}{$H+}{$ENDIF}
+uses
+
+sysutils, LibTar, classes, dialogs, zstream;
+
+function deTGZ (lFilename: string): string;
+function isTGZ (var lStr: string): boolean;
+
+implementation
+
+function isTGZ (var lStr: string): boolean;
+var lExt: string;
+begin
+   lExt := ExtractFileExt(lStr);
+   lExt := UpperCase(lExt);
+   if (lExt='.TGZ') then
+      Result := true
+   else
+       Result := false;
+end;
+
+procedure Extract (var lTarFile: string; lOverwrite: boolean); //extract target
+VAR
+  TA        : TTarArchive;
+  DirRec    : TTarDirRec;
+  lPos,lLen,lnumFilesTotal,lnumFilesCompleted,lPct: longint;
+  lStr,lOutDir,lLocalDir,lFileName,lNewDir,lTarName : String;
+begin
+            lOutDir := extractfiledir(lTarFile);
+            //next Count files for progress bar....
+            lnumFilesTotal := 0;
+            TA := TTarArchive.Create (lTarFile);
+            TRY
+               TA.Reset;
+               TA.SetFilePos (0);
+               TA.FindNext (DirRec);
+               repeat
+                     inc(lnumFilesTotal);
+               until not TA.FindNext (DirRec);
+            FINALLY
+                   TA.Free;
+            END;
+            //finished counting files
+            //next: extract files...
+            lnumFilesCompleted := 0;
+            //FProgress := 0;
+            TA := TTarArchive.Create (lTarFile);
+            TRY
+               TA.Reset;
+               TA.SetFilePos (0);
+               TA.FindNext (DirRec);
+            repeat
+              inc(lNumFilesCompleted);
+              {lPct := round(lNumFilesCOmpleted/lNumfilesTotal*100);
+              if lPct > FProgress then begin //only update progress bar 100 times: do not waste time updating screen
+                 FProgress := lPct;
+                 DoOnProgress;
+              end;}
+              if DirRec.Name <> '' then begin
+               //Screen.Cursor := crHourGlass;
+               TRY
+                  //filename change '/' to '\'
+                  lTarName := '';
+                  lLen := length(DirRec.name);
+                  for lPos := 1 to lLen do begin
+                     if (DirRec.Name[lPos]='/') or (DirRec.Name[lPos]='\') then
+                        lTarName := lTarName + pathdelim//'\'
+                     else if (DirRec.Name[lPos]=':') then
+
+                     else
+                         lTarName := lTarName + DirRec.Name[lPos];
+                  end;
+                  lFilename := lOutDir+pathdelim+lTarName;
+                  lLocalDir := extractfiledir(lFileName);
+                  if (DirectoryExists(lLocalDir)) then begin
+                    if lOverwrite{(lProceed = mrYes) or (lProceed = mrYesToAll)} then begin
+                     if (length(lFilename)>2) and (lFilename[length(lFilename)] = pathdelim)   then begin
+                        lLen := length(lFilename)-1;
+                        lStr := lFilename;
+                        lFilename := '';
+                        for lPos := 1 to lLen do
+                            lFilename := lFilename+lStr[lPos];
+                        if not DirectoryExists(lFilename) then begin
+                           mkdir (lFilename);
+                        end;
+                     end else
+                         TA.ReadFile (lFileName);
+                    end; //proceed
+                  end else begin
+                     lLen := length(lTarName);
+                     lPos := 1;
+                     if (lLen >= 1) and (lTarName[1] = pathdelim) then inc(lPos);
+                     lNewDir := lOutDir+pathdelim;
+                     while lPos <= lLen do begin
+                          if (lTarName[lPos] = pathdelim) then begin
+                             //showmessage('creating directory:'+lNewDir);
+                             if not DirectoryExists(lNewDir) then
+                                mkdir(lNewDir);
+                             lNewDir := lNewDir + pathdelim;
+                          end else
+                           lNewDir := lNewDir + lTarName[lPos];
+                          inc(lPos);
+                     end;
+                     if (lFileName[length(lFileName)] <> pathdelim) and  (DirectoryExists(lLocalDir)) and (not Fileexists(lFileName)) then begin
+                        TA.ReadFile (lFileName)
+                     end;
+                  end;
+               FINALLY
+                      //Screen.Cursor := crDefault;
+               END;
+              end;
+            until not TA.FindNext (DirRec);
+            FINALLY
+                   TA.Free;
+            END;
+end;
+
+function UnGZipFile(inname, outname:string):string;
+var
+ArchStream : TGZFileStream;
+FileStream : TFileStream;
+bytescopied: integer;
+buf: array[0..65535] of byte;
+chunksize : integer;
+begin
+chunksize := SizeOf(buf);
+ArchStream := TGZFileStream.Create(inname,gzOpenRead);
+FileStream := TFileStream.Create(outname,fmOpenWrite or fmCreate );
+repeat
+    bytescopied := ArchStream.read(buf,chunksize);
+    FileStream.Write(buf,bytescopied) ;
+ until bytescopied < chunksize;
+   FileStream.Free;
+   ArchStream.Free;
+end;
+
+function DeTGZ (lFilename: string): string;
+var
+ lPath,lName,lExt,lOutPath,lTempDir,lTarName,lDicomName: string;
+begin
+     result := '';
+     if (not fileexists(lFilename)) or (not isTGZ(lFilename)) then
+        exit;
+     lPath := ExtractFilePath(lFilename);
+     lExt := ExtractFileExt(lFilename);
+     lName := ChangeFileExt (ExtractFileName (lFilename), '');
+     lOutPath := lPath+lName;
+     if DirectoryExists(lOutPath) then begin
+     	if IsConsole then
+           writeln( 'Unable to extract TGZ file - folder exists '+lOutpath)
+        else
+      		ShowMessage('Unable to extract TGZ file - folder exists '+lOutpath);
+         exit;
+     end;
+     MkDir(lOutPath);
+     lTempDir := lOutPath+pathdelim+'temp';
+     MkDir(lTempDir);
+     lTarName := lTempDir+Pathdelim+lName+'.tar';
+     UnGZipFile( lFilename,lTarName);
+     Extract (lTarName,true);
+     deletefile(lTarName);
+     result := lOutPath;
+end;
+
+end.
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..c956305
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,36 @@
+The following files are from different authors and have their own licenses
+
+nifti.h, nifti1_io.h/nifti1_io.c is public domain
+  http://niftilib.sourceforge.net
+  http://sourceforge.net/projects/niftilib/files/latest/download
+ujpeg.h/ujpeg.c see web site for licenses
+  http://keyj.emphy.de/nanojpeg/
+miniz.c is public domain (http://unlicense.org)
+ https://code.google.com/p/miniz/
+
+---
+The Software has been developed for research purposes only and is not a clinical tool
+Copyright (c) 2014-2016 Chris Rorden. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright owner nor the name of this project
+   (dcm2niix) may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT OWNER ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/qtGui/Q_DebugStream.h b/qtGui/Q_DebugStream.h
new file mode 100644
index 0000000..368417a
--- /dev/null
+++ b/qtGui/Q_DebugStream.h
@@ -0,0 +1,71 @@
+//http://stackoverflow.com/questions/10308425/redirect-stdcout-to-a-qtextedit
+#ifndef QDEBUGSTREAM_H
+#define QDEBUGSTREAM_H
+
+#include <iostream>
+#include <streambuf>
+#include <string>
+
+#include "qtextedit.h"
+
+class QDebugStream : public std::basic_streambuf<char>
+{
+public:
+ QDebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
+ {
+  log_window = text_edit;
+  m_old_buf = stream.rdbuf();
+  stream.rdbuf(this);
+ }
+ ~QDebugStream()
+ {
+  // output anything that is left
+  if (!m_string.empty())
+   log_window->append(m_string.c_str());
+
+  m_stream.rdbuf(m_old_buf);
+ }
+
+protected:
+ virtual int_type overflow(int_type v)
+ {
+  if (v == '\n')
+  {
+   log_window->append(m_string.c_str());
+   m_string.erase(m_string.begin(), m_string.end());
+  }
+  else
+   m_string += v;
+
+  return v;
+ }
+
+ virtual std::streamsize xsputn(const char *p, std::streamsize n)
+ {
+  m_string.append(p, p + n);
+
+  int pos = 0;
+  while (pos != std::string::npos)
+  {
+   pos = m_string.find('\n');
+   if (pos != std::string::npos)
+   {
+    std::string tmp(m_string.begin(), m_string.begin() + pos);
+    log_window->append(tmp.c_str());
+    m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
+   }
+  }
+
+  return n;
+ }
+
+private:
+ std::ostream &m_stream;
+ std::streambuf *m_old_buf;
+ std::string m_string;
+
+
+  QTextEdit* log_window;
+    };
+
+#endif
\ No newline at end of file
diff --git a/qtGui/Q_DebugStream_new.h b/qtGui/Q_DebugStream_new.h
new file mode 100644
index 0000000..7b0b83e
--- /dev/null
+++ b/qtGui/Q_DebugStream_new.h
@@ -0,0 +1,97 @@
+//http://www.qtforum.org/article/678/redirecting-cout-cerr-to-qdebug.html
+#ifndef Q_DEBUGSTREAM_H
+#define Q_DEBUGSTREAM_H
+
+#include <iostream>
+#include <streambuf>
+#include <string>
+
+#include "QTextEdit.h"
+
+class Q_DebugStream : public std::basic_streambuf<char>
+{
+public:
+    Q_DebugStream(std::ostream &stream, QTextEdit* text_edit) : m_stream(stream)
+    {
+        log_window = text_edit;
+        m_old_buf = stream.rdbuf();
+        stream.rdbuf(this);
+    }
+
+    ~Q_DebugStream()
+    {
+        m_stream.rdbuf(m_old_buf);
+    }
+
+    static void registerQDebugMessageHandler(){
+        qInstallMessageHandler(myQDebugMessageHandler);
+    }
+
+private:
+
+    static void myQDebugMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
+    {
+        std::cout << msg.toStdString().c_str();
+    }
+
+protected:
+
+    //This is called when a std::endl has been inserted into the stream
+    virtual int_type overflow(int_type v)
+    {
+        if (v == '\n') {
+            log_window->append("");
+        }
+        return v;
+    }
+
+    /*virtual std::streamsize xsputn(const char *p, std::streamsize n)
+    {
+        std::string m_string;
+     m_string.append(p, p + n);
+
+     int pos = 0;
+     while (pos != std::string::npos)
+     {
+      pos = m_string.find('\n');
+      if (pos != std::string::npos)
+      {
+       std::string tmp(m_string.begin(), m_string.begin() + pos);
+       log_window->append(tmp.c_str());
+       m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
+      }
+     }
+     return n;
+    }*/
+
+    virtual std::streamsize xsputn(const char *p, std::streamsize n)
+    {
+    QString str(p);
+    //str.remove(QRegExp("[^a-zA-Z/0-9. \\d\\s]"));
+    //QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
+    //str.remove(QRegExp(QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\\[\\\]\\\\]")));
+    if (str.contains("\n")){
+            QStringList strSplitted = str.split("\n");
+
+            log_window->moveCursor (QTextCursor::End);
+            log_window->insertPlainText (strSplitted.at(0)); //Index 0 is still on the same old line
+
+            for(int i = 1; i < strSplitted.size(); i++){
+                log_window->append(strSplitted.at(i));
+            }
+        }else{
+            log_window->moveCursor (QTextCursor::End);
+            log_window->insertPlainText (str);
+            //log_window->append(str);
+        }
+        return n;
+    }
+
+private:
+    std::ostream &m_stream;
+    std::streambuf *m_old_buf;
+    QTextEdit* log_window;
+};
+
+
+#endif // Q_DEBUGSTREAM_H
diff --git a/qtGui/dcm2.pro b/qtGui/dcm2.pro
new file mode 100644
index 0000000..16dadb9
--- /dev/null
+++ b/qtGui/dcm2.pro
@@ -0,0 +1,44 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2014-04-22T13:13:08
+#
+#-------------------------------------------------
+
+QT       += core gui
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+DEFINES += UNICODE
+
+#link zlib for Windows OS see https://gitorious.org/tiled/stefanbellers-tiled-qt/commit/2f9cb77ae082223ef2324965cd8f45a2f245098c
+#INCLUDEPATH += $$[QT_INSTALL_PREFIX]/src/3rdparty/zlib
+#INCLUDEPATH += ../zlib
+LIBS += -lz
+
+TARGET = dcm2
+TEMPLATE = app
+
+#our .c files have c++ code, but old versions of xcode will complain if we use .cpp instead of .c
+QMAKE_CFLAGS +=  -x c++
+QMAKE_CXXFLAGS += -x c++
+
+
+#we need to redirect the std:cout instead of printf
+QMAKE_CFLAGS +=  -DmyUseCOut
+
+SOURCES += main.cpp\
+        mainwindow.cpp \
+    nifti1_io_core.c \
+   nii_dicom_batch.c \
+    nii_dicom.c \
+    nii_ortho.c \
+
+HEADERS  += mainwindow.h \
+    nifti1_io_core.h \
+    nifti1.h \
+    nii_dicom_batch.h \
+    nii_dicom.h \
+    nii_ortho.h \
+    tinydir.h \
+    Q_DebugStream.h
+
+FORMS    += mainwindow.ui
diff --git a/qtGui/dcm2.pro.user b/qtGui/dcm2.pro.user
new file mode 100644
index 0000000..f1f614e
--- /dev/null
+++ b/qtGui/dcm2.pro.user
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 3.1.0, 2014-05-09T16:07:16. -->
+<qtcreator>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap"/>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.2.1 clang 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.2.1 clang 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.521.clang_64.essentials_kit</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/rorden/Documents/cocoa/dcm2/build-dcm2-Desktop_Qt_5_2_1_clang_64bit-Debug</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/rorden/Documents/cocoa/dcm2/build-dcm2-Desktop_Qt_5_2_1_clang_64bit-Release</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">dcm2</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/Users/rorden/Documents/cocoa/dcm2/qtGui/dcm2.pro</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">dcm2.pro</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal">false</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
+    <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">1</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.EnvironmentId</variable>
+  <value type="QByteArray">{6365facd-1c54-4b35-8fef-327797cb09f7}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">15</value>
+ </data>
+</qtcreator>
diff --git a/qtGui/main.cpp b/qtGui/main.cpp
new file mode 100644
index 0000000..e536237
--- /dev/null
+++ b/qtGui/main.cpp
@@ -0,0 +1,12 @@
+#include "mainwindow.h"
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+
+    QApplication a(argc, argv);
+
+    MainWindow w;
+    w.show();
+    return a.exec();
+}
diff --git a/qtGui/mainwindow.cpp b/qtGui/mainwindow.cpp
new file mode 100644
index 0000000..d42a1ee
--- /dev/null
+++ b/qtGui/mainwindow.cpp
@@ -0,0 +1,121 @@
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include <QFileDialog>
+#include <QDragEnterEvent>
+#include <QClipboard>
+#include "nii_dicom_batch.h"
+#include "Q_DebugStream.h"
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#if defined(_WIN64) || defined(_WIN32)
+#include <windows.h> //write to registry
+#endif
+
+void MainWindow::showPrefs(bool doClear) {
+    ui->compressCheck->setChecked(opts.isGz);
+    ui->outputFilenameEdit->setText(opts.filename);
+    size_t kLen = 40;
+    if (strlen(opts.outdir) < 1)
+        ui->folderButton->setText("input folder");
+    else if (strlen(opts.outdir) > kLen) {
+        char cString[kLen+1];
+        memcpy(cString,opts.outdir,kLen-1);
+        cString[kLen-1] = 0;
+        ui->folderButton->setText(cString);
+    } else
+        ui->folderButton->setText(opts.outdir);
+    this->showExampleFilename( doClear);
+}
+
+void MainWindow::processFile(const QString &arg1) {
+    //convert folder (DICOM) or file (PAR/REC) to NIFTI format
+    ui->theTextView->clear();
+    struct TDCMopts optsTemp;
+    optsTemp = opts; //conversion may change values like the outdir (if not specified)
+    strcpy(optsTemp.indir, arg1.toStdString().c_str());
+    printf("processing %s\n", arg1.toStdString().c_str());
+    clock_t start = clock();
+    nii_loadDir (&(optsTemp));
+    std::cout<< "Required "<<((float)(clock()-start))/CLOCKS_PER_SEC<<"seconds."<<std::endl;
+}
+
+void MainWindow::showExampleFilename(bool doClear) {
+    char niiFilename[1024];
+    nii_createDummyFilename(niiFilename, opts);
+    if (doClear) ui->theTextView->clear();
+    std::cout<<niiFilename<<std::endl;
+    std::cout<< "Version "<<kDCMvers<<std::endl;
+}
+
+void MainWindow::on_outputFilenameEdit_textEdited(const QString &arg1)
+{
+    strcpy(opts.filename, arg1.toStdString().c_str());
+    this->showExampleFilename(true);
+}
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
+{
+    ui->setupUi(this);
+    ui->theTextView->setReadOnly(true);
+    setAcceptDrops(true);
+    new QDebugStream(std::cout, ui->theTextView); //Redirect Console output to QTextEdit
+    //new Q_DebugStream(std::cout, ui->theTextView); //Redirect Console output to QTextEdit
+    //Q_DebugStream::registerQDebugMessageHandler(); //Redirect qDebug() output to QTextEdit
+    const char *appPath =QCoreApplication::applicationFilePath().toStdString().c_str();
+    readIniFile (&opts, &appPath);
+    this->showPrefs(false);
+}
+
+MainWindow::~MainWindow()
+{
+    saveIniFile (opts); //save preferences
+    delete ui;
+}
+
+void MainWindow::on_compressCheck_clicked()
+{
+    opts.isGz = ui->compressCheck->isChecked();
+    this->showExampleFilename(true);
+}
+
+void MainWindow::on_actionCopy_triggered()
+{
+    QString newText = ui->theTextView->toPlainText();
+    QApplication::clipboard()->setText(newText);
+}
+
+void MainWindow::on_actionPAR_REC_to_NIfTI_triggered()
+{
+    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Philips PAR image"),directory.path(),
+        tr("PAR Files (*.par *.PAR)"));
+    if ( fileName.isNull()  ) return;
+    processFile(fileName);
+}
+
+void MainWindow::on_actionDICOM_to_NIfTI_triggered()
+{
+    QString path = QFileDialog::getExistingDirectory (this, tr("Directory"), directory.path());
+    if ( path.isNull() ) return;
+    directory.setPath(path);
+    processFile(path);
+}
+
+void MainWindow::dragEnterEvent(QDragEnterEvent *ev)
+{
+    ev->accept();
+}
+
+void MainWindow::dropEvent(QDropEvent *event)
+{
+    QList<QUrl> urls = event->mimeData()->urls();
+    event->acceptProposedAction();
+    processFile(urls[0].toLocalFile());
+}
+
+void MainWindow::on_folderButton_clicked()
+{
+    QString path = QFileDialog::getExistingDirectory (this, tr("Select output folder (cancel to use input folder)"), directory.path());
+    if ( path.isNull() )  path = "";
+    strcpy(opts.outdir, path.toStdString().c_str());
+    directory.setPath(path);
+    this->showPrefs(true);
+}
diff --git a/qtGui/mainwindow.h b/qtGui/mainwindow.h
new file mode 100644
index 0000000..d37cd79
--- /dev/null
+++ b/qtGui/mainwindow.h
@@ -0,0 +1,60 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QDir>
+#include <QDropEvent>
+#include <QUrl>
+#include <QDebug>
+#include <QMimeData>
+//#include <QProcess>
+#include "nii_dicom_batch.h"
+
+namespace Ui {
+class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+    //QProcess *myProcess;
+    //Ui::MainWindow *ui;
+        struct TDCMopts opts;
+private slots:
+    //void on_pushButton_clicked();
+
+    void on_compressCheck_clicked();
+
+    void on_actionCopy_triggered();
+
+    void on_actionPAR_REC_to_NIfTI_triggered();
+
+    void on_actionDICOM_to_NIfTI_triggered();
+
+
+    //void on_lineEdit_textEdited(const QString &arg1);
+    //void updateText();
+    //void readData();
+void processFile(const QString &arg1);
+
+
+    void on_folderButton_clicked();
+
+    void on_outputFilenameEdit_textEdited(const QString &arg1);
+
+private:
+    Ui::MainWindow *ui;
+    QDir directory;
+protected:
+    void showExampleFilename(bool doClear) ;
+    void showPrefs(bool doClear);
+    void dropEvent(QDropEvent *ev);
+    void dragEnterEvent(QDragEnterEvent *ev);
+    //StdoutRedirector *redirector;
+};
+
+#endif // MAINWINDOW_H
diff --git a/qtGui/mainwindow.ui b/qtGui/mainwindow.ui
new file mode 100644
index 0000000..32aff87
--- /dev/null
+++ b/qtGui/mainwindow.ui
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>760</width>
+    <height>480</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>720</width>
+    <height>120</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>dcm2</string>
+  </property>
+  <widget class="QWidget" name="centralWidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <property name="leftMargin">
+     <number>4</number>
+    </property>
+    <property name="topMargin">
+     <number>4</number>
+    </property>
+    <property name="rightMargin">
+     <number>4</number>
+    </property>
+    <property name="bottomMargin">
+     <number>4</number>
+    </property>
+    <item row="0" column="0">
+     <widget class="QCheckBox" name="compressCheck">
+      <property name="maximumSize">
+       <size>
+        <width>89</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="toolTip">
+       <string>If checked, images will be stored as compressed .nii.gz</string>
+      </property>
+      <property name="layoutDirection">
+       <enum>Qt::RightToLeft</enum>
+      </property>
+      <property name="text">
+       <string>Compress</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1">
+     <widget class="QLabel" name="label">
+      <property name="minimumSize">
+       <size>
+        <width>87</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>87</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="text">
+       <string> Output name</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="2">
+     <widget class="QLineEdit" name="outputFilenameEdit">
+      <property name="minimumSize">
+       <size>
+        <width>231</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>231</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="toolTip">
+       <string>Name of NIfTI images (%c= comments, %f folder name, %n name, %p protocol, %s series, %t time)</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="3">
+     <widget class="QLabel" name="label_2">
+      <property name="minimumSize">
+       <size>
+        <width>96</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="maximumSize">
+       <size>
+        <width>96</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="text">
+       <string> Output folder</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="4">
+     <widget class="QPushButton" name="folderButton">
+      <property name="minimumSize">
+       <size>
+        <width>200</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="toolTip">
+       <string>Set location for converted images (or choose to have files saved to the same location as the input images)</string>
+      </property>
+      <property name="text">
+       <string>Output Folder</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="0" colspan="5">
+     <widget class="QTextEdit" name="theTextView">
+      <property name="maximumSize">
+       <size>
+        <width>16777215</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Sunken</enum>
+      </property>
+      <property name="lineWidth">
+       <number>1</number>
+      </property>
+      <property name="autoFormatting">
+       <set>QTextEdit::AutoBulletList</set>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menuBar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>760</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuDcm2">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionDICOM_to_NIfTI"/>
+    <addaction name="actionPAR_REC_to_NIfTI"/>
+   </widget>
+   <widget class="QMenu" name="menuEdit">
+    <property name="title">
+     <string>Edit</string>
+    </property>
+    <addaction name="actionCopy"/>
+   </widget>
+   <addaction name="menuDcm2"/>
+   <addaction name="menuEdit"/>
+  </widget>
+  <action name="actionDICOM_to_NIfTI">
+   <property name="text">
+    <string>DICOM to NIfTI...</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+D</string>
+   </property>
+  </action>
+  <action name="actionPAR_REC_to_NIfTI">
+   <property name="text">
+    <string>PAR/REC to NIfTI...</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+P</string>
+   </property>
+  </action>
+  <action name="actionCopy">
+   <property name="text">
+    <string>Copy</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+C</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qtGui/nifti1_io_core.c b/qtGui/nifti1_io_core.c
new file mode 100644
index 0000000..1469ef3
--- /dev/null
+++ b/qtGui/nifti1_io_core.c
@@ -0,0 +1,472 @@
+#include "nifti1_io_core.h"
+#include <math.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+#include <unistd.h>
+#include <stdio.h>
+
+
+void nifti_swap_8bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    register unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+7;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 8;
+    }
+    return ;
+}
+
+void nifti_swap_4bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    register unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+3;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 4;
+    }
+    return ;
+}
+
+void nifti_swap_2bytes( size_t n , void *ar )    // 2 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp1 = (unsigned char *)ar, * cp2 ;
+    unsigned char   tval;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp2 = cp1 + 1;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1 += 2;
+    }
+    return ;
+}
+
+int isSameFloat (float a, float b) {
+    return (fabs (a - b) <= FLT_EPSILON);
+}
+
+vec3 setVec3(float x, float y, float z)
+{
+    vec3 v = {x, y, z};
+    return v;
+}
+
+vec4 setVec4(float x, float y, float z)
+{
+    vec4 v= {x, y, z, 1};
+    return v;
+}
+
+vec3 crossProduct(vec3 u, vec3 v)
+{
+    return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2],
+                   -u.v[0]*v.v[2] + v.v[0]*u.v[2],
+                   u.v[0]*v.v[1] - v.v[0]*u.v[1]);
+}
+
+float dotProduct(vec3 u, vec3 v)
+{
+    return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]);
+}
+
+vec3 nifti_vect33_norm (vec3 v) { //normalize vector length
+    vec3 vO = v;
+    float vLen = sqrt( (v.v[0]*v.v[0])
+                      + (v.v[1]*v.v[1])
+                      + (v.v[2]*v.v[2]));
+    if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero
+    for (int i = 0; i < 3; i++)
+        vO.v[i] = v.v[i]/vLen;
+    return vO;
+}
+
+vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix
+    vec3 vO;
+    for (int i=0; i<3; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<3; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix
+    vec4 vO;
+    for (int i=0; i<4; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<4; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) {
+    //create NIfTI header based on values from DICOM header
+    //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1
+    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] );
+    /* 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 ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ;
+    } else {
+        Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ;
+    }
+    /* normalize row 2 */
+    val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ;
+    if( val > 0.0l ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ;
+    } else {
+        Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ;
+    }
+    /* row 3 is the cross product of rows 1 and 2*/
+    Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ;  /* cross */
+    Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ;  /* product */
+    Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ;
+    Q = nifti_mat33_transpose(Q);
+    if (nifti_mat33_determ(Q) < 0.0) {
+        Q.m[0][2] = -Q.m[0][2];
+        Q.m[1][2] = -Q.m[1][2];
+        Q.m[2][2] = -Q.m[2][2];
+    }
+    //next scale matrix
+    LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]);
+    Q = nifti_mat33_mul(Q,diagVox);
+    mat44 Q44; //4x4 matrix includes translations
+    LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1],
+               Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2],
+               Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]);
+    return Q44;
+}
+
+float nifti_mat33_determ( mat33 R )   /* determinant of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 ;
+    /*  INPUT MATRIX:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  /* [ r11 r12 r13 ] */
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  /* [ r21 r22 r23 ] */
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  /* [ r31 r32 r33 ] */
+    return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33
+                   +r21*r32*r13+r31*r12*r23-r31*r22*r13) ;
+}
+
+mat33 nifti_mat33_mul( mat33 A , mat33 B )  /* multiply 2 3x3 matrices */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 C ; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j] ;
+    return C ;
+}
+
+mat44 nifti_mat44_mul( mat44 A , mat44 B )  /* multiply 2 3x3 matrices */
+{
+    mat44 C ; int i,j ;
+    for( i=0 ; i < 4 ; i++ )
+        for( j=0 ; j < 4; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j]
+            + A.m[i][3] * B.m[3][j];
+    return C ;
+}
+
+mat33 nifti_mat33_transpose( mat33 A )  /* transpose 3x3 matrix */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 B; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            B.m[i][j] =  A.m[j][i];
+    return B;
+}
+
+mat33 nifti_mat33_inverse( mat33 R )   /* inverse of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ;
+    mat33 Q ;
+    //  INPUT MATRIX:
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    return Q ;
+}
+
+float nifti_mat33_rownorm( mat33 A )  // max row norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ;
+    r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ;
+    r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+float nifti_mat33_colnorm( mat33 A )  // max column norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ;
+    r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ;
+    r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+mat33 nifti_mat33_polar( mat33 A )
+{
+    mat33 X , Y , Z ;
+    float alp,bet,gam,gmi , dif=1.0 ;
+    int k=0 ;
+    X = A ;
+    // force matrix to be nonsingular
+    gam = nifti_mat33_determ(X) ;
+    while( gam == 0.0 ){        // perturb matrix
+        gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ;
+        X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ;
+        gam = nifti_mat33_determ(X) ;
+    }
+    while(1){
+        Y = nifti_mat33_inverse(X) ;
+        if( dif > 0.3 ){     // far from convergence
+            alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ;
+            bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ;
+            gam = sqrt( bet / alp ) ;
+            gmi = 1.0 / gam ;
+        } else
+            gam = gmi = 1.0 ;  // close to convergence
+        Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ;
+        Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ;
+        Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ;
+        Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ;
+        Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ;
+        Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ;
+        Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ;
+        Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ;
+        Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ;
+        dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1])
+        +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0])
+        +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2])
+        +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1])
+        +fabs(Z.m[2][2]-X.m[2][2])                          ;
+        k = k+1 ;
+        if( k > 100 || dif < 3.e-6 ) break ;  // convergence or exhaustion
+        X = Z ;
+    }
+    return Z ;
+}
+
+void nifti_mat44_to_quatern( mat44 R ,
+                            float *qb, float *qc, float *qd,
+                            float *qx, float *qy, float *qz,
+                            float *dx, float *dy, float *dz, float *qfac )
+{
+    double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ;
+    double xd,yd,zd , a,b,c,d ;
+    mat33 P,Q ;
+    // offset outputs are read write out of input matrix
+    ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ;
+    // load 3x3 matrix into local variables */
+    r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ;
+    r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ;
+    r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ;
+    // compute lengths of each column; these determine grid spacings
+    xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ;
+    yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ;
+    zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ;
+    // if a column length is zero, patch the trouble
+    if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; }
+    if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; }
+    if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; }
+    // assign the output lengths */
+    ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ;
+    // normalize the columns */
+    r11 /= xd ; r21 /= xd ; r31 /= xd ;
+    r12 /= yd ; r22 /= yd ; r32 /= yd ;
+    r13 /= zd ; r23 /= zd ; r33 /= zd ;
+    /* At this point, the matrix has normal columns, but we have to allow
+     for the fact that the hideous user may not have given us a matrix
+     with orthogonal columns.
+     So, now find the orthogonal matrix closest to the current matrix.
+     One reason for using the polar decomposition to get this
+     orthogonal matrix, rather than just directly orthogonalizing
+     the columns, is so that inputting the inverse matrix to R
+     will result in the inverse orthogonal matrix at this point.
+     If we just orthogonalized the columns, this wouldn't necessarily hold. */
+    Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q
+    Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ;
+    Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ;
+    P = nifti_mat33_polar(Q) ;  // P is orthog matrix closest to Q
+    r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload
+    r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ;
+    r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ;
+    //                            [ r11 r12 r13 ]
+    // at this point, the matrix  [ r21 r22 r23 ] is orthogonal
+    //                            [ r31 r32 r33 ]
+    // compute the determinant to determine if it is proper
+    zd = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;  // should be -1 or 1
+    if( zd > 0 ){             // proper
+        ASSIF(qfac,1.0) ;
+    } else {                  // improper ==> flip 3rd column
+        ASSIF(qfac,-1.0) ;
+        r13 = -r13 ; r23 = -r23 ; r33 = -r33 ;
+    }
+    // now, compute quaternion parameters
+    a = r11 + r22 + r33 + 1.0l ;
+    if( a > 0.5l ){                // simplest case
+        a = 0.5l * sqrt(a) ;
+        b = 0.25l * (r32-r23) / a ;
+        c = 0.25l * (r13-r31) / a ;
+        d = 0.25l * (r21-r12) / a ;
+    } else {                       // trickier case
+        xd = 1.0 + r11 - (r22+r33) ;  // 4*b*b
+        yd = 1.0 + r22 - (r11+r33) ;  // 4*c*c
+        zd = 1.0 + r33 - (r11+r22) ;  // 4*d*d
+        if( xd > 1.0 ){
+            b = 0.5l * sqrt(xd) ;
+            c = 0.25l* (r12+r21) / b ;
+            d = 0.25l* (r13+r31) / b ;
+            a = 0.25l* (r32-r23) / b ;
+        } else if( yd > 1.0 ){
+            c = 0.5l * sqrt(yd) ;
+            b = 0.25l* (r12+r21) / c ;
+            d = 0.25l* (r23+r32) / c ;
+            a = 0.25l* (r13-r31) / c ;
+        } else {
+            d = 0.5l * sqrt(zd) ;
+            b = 0.25l* (r13+r31) / d ;
+            c = 0.25l* (r23+r32) / d ;
+            a = 0.25l* (r21-r12) / d ;
+        }
+        //        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; }
+        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded...
+    }
+    ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ;
+    return ;
+}
+
+mat44 nifti_quatern_to_mat44( float qb, float qc, float qd,
+                             float qx, float qy, float qz,
+                             float dx, float dy, float dz, float qfac )
+{
+    mat44 R ;
+    double a,b=qb,c=qc,d=qd , xd,yd,zd ;
+    
+    /* last row is always [ 0 0 0 1 ] */
+    
+    R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ;
+    
+    /* compute a parameter from b,c,d */
+    
+    a = 1.0l - (b*b + c*c + d*d) ;
+    if( a < 1.e-7l ){                   /* special case */
+        a = 1.0l / sqrt(b*b+c*c+d*d) ;
+        b *= a ; c *= a ; d *= a ;        /* normalize (b,c,d) vector */
+        a = 0.0l ;                        /* a = 0 ==> 180 degree rotation */
+    } else{
+        a = sqrt(a) ;                     /* angle = 2*arccos(a) */
+    }
+    
+    /* load rotation matrix, including scaling factors for voxel sizes */
+    
+    xd = (dx > 0.0) ? dx : 1.0l ;       /* make sure are positive */
+    yd = (dy > 0.0) ? dy : 1.0l ;
+    zd = (dz > 0.0) ? dz : 1.0l ;
+    
+    if( qfac < 0.0 ) zd = -zd ;         /* left handedness? */
+    
+    R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ;
+    R.m[0][1] = 2.0l * (b*c-a*d        ) * yd ;
+    R.m[0][2] = 2.0l * (b*d+a*c        ) * zd ;
+    R.m[1][0] = 2.0l * (b*c+a*d        ) * xd ;
+    R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ;
+    R.m[1][2] = 2.0l * (c*d-a*b        ) * zd ;
+    R.m[2][0] = 2.0l * (b*d-a*c        ) * xd ;
+    R.m[2][1] = 2.0l * (c*d+a*b        ) * yd ;
+    R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ;
+    
+    /* load offsets */
+    
+    R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ;
+    
+    return R ;
+}
+
+mat44 nifti_mat44_inverse( mat44 R )
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ;
+    mat44 Q ;
+    /*  INPUT MATRIX IS:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 v1 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 v2 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 v3 ]
+    v1  = R.m[0][3]; v2  = R.m[1][3]; v3  = R.m[2][3];  // [  0   0   0   1 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3
+                      -r22*v1*r33-r32*r13*v2+r32*v1*r23) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3
+                      +r21*v1*r33+r31*r13*v2-r31*v1*r23) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3
+                      -r21*r32*v1-r31*r12*v2+r31*r22*v1) ;
+    Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ;
+    Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0
+    return Q ;
+}
+
+
+
+
+
+
+
+
+
diff --git a/qtGui/nii_dicom.c b/qtGui/nii_dicom.c
new file mode 100644
index 0000000..9e5e381
--- /dev/null
+++ b/qtGui/nii_dicom.c
@@ -0,0 +1,1852 @@
+//#define MY_DEBUG
+
+#include "nifti1.h"
+#include "nii_dicom.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h> //toupper
+#include <math.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <float.h>
+#include <stdint.h>
+#include "nifti1_io_core.h"
+#ifdef myUseCOut
+#include <iostream>
+#endif
+
+#ifndef M_PI
+#define M_PI           3.14159265358979323846
+#endif
+
+#ifdef MY_DEBUG
+float deFuzz(float v) {
+    if (fabs(v) < 0.00001)
+        return 0;
+    else
+        return v;
+    
+}
+
+void reportMat33(char *str, mat33 A) {
+    printf("%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]));
+}
+
+void reportMat44(char *str, mat44 A) {
+    printf("%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]));
+}
+#endif
+
+
+int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R){
+    //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped
+    if (h->dim[3] < 2) return 0; //don't care direction for single slice
+    int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column
+    if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; //
+    if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition
+    float pos = NAN;
+    if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist
+        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);
+#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);
+#endif
+    }
+    if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) )
+        pos = d.stackOffcentre[iSL];
+    if (isnan(pos) && ( !isnan(d.lastScanLoc)) )
+        pos = d.lastScanLoc;
+    vec4 x;
+    x.v[0] = 0; x.v[1] = 0; x.v[2]=h->dim[3]-1; x.v[3] = 1;
+    vec4 pos1v = nifti_vect44mat44_mul(x, *R);
+    float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0
+    bool flip = false;
+    if (!isnan(pos)) // we have real SliceLocation for last slice or volume center
+        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
+#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
+    }
+    if (flip) {
+        for (int i = 0; i < 4; i++)
+            R->m[i][2] = -R->m[i][2];
+    }
+#ifdef MY_DEBUG
+    printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
+    reportMat44("Rout",*R);
+    printf("iSL = %d\n",iSL);
+    printf(" pos1 = %f\n",pos1);
+#endif
+    if (flip)
+        return -iSL;
+    else
+        return iSL;
+} //verify_slice_dir()
+
+void setQSForm(struct nifti_1_header *h, mat44 Q44) {
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->srow_x[0] = Q44.m[0][0];
+    h->srow_x[1] = Q44.m[0][1];
+    h->srow_x[2] = Q44.m[0][2];
+    h->srow_x[3] = Q44.m[0][3];
+    h->srow_y[0] = Q44.m[1][0];
+    h->srow_y[1] = Q44.m[1][1];
+    h->srow_y[2] = Q44.m[1][2];
+    h->srow_y[3] = Q44.m[1][3];
+    h->srow_z[0] = Q44.m[2][0];
+    h->srow_z[1] = Q44.m[2][1];
+    h->srow_z[2] = Q44.m[2][2];
+    h->srow_z[3] = Q44.m[2][3];
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    h->qform_code = NIFTI_XFORM_SCANNER_ANAT;
+} //setQSForm()
+
+
+
+int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_1_header *h) { //fill header s and q form
+    //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+    //returns sliceDirection: 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
+    h->sform_code = NIFTI_XFORM_UNKNOWN;
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    bool isOK = false;
+    for (int i = 1; i <= 6; i++)
+        if (d.orient[i] != 0.0) isOK = true;
+    if (!isOK) return sliceDir;
+    mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM);
+    if (d.CSA.mosaicSlices > 1) {
+        double nRowCol = ceil(sqrt(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;
+        Q44.m[0][3] =(Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3];
+        Q44.m[1][3] =(Q44.m[1][0]*lFactorX)+(Q44.m[1][1]*lFactorY)+Q44.m[1][3];
+        Q44.m[2][3] =(Q44.m[2][0]*lFactorX)+(Q44.m[2][1]*lFactorY)+Q44.m[2][3];
+        
+        /* #ifdef obsolete_mosaic_flip
+         double val = d.xyzDim[2]/nRowCol; //obsolete!!!
+         //Q44 now equals 'dicom_to_patient' in spm_dicom_convert
+         mat44 patient_to_tal, analyze_to_dicom;
+         LOAD_MAT44(patient_to_tal, -1.0l,0.0l,0.0l,0.0l, 0.0l,-1.0l,0.0l,0.0l, 0.0l,0.0l,1.0l,0.0l);
+         LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,-1.0l, 0.0l,-1.0l,0.0l,val, 0.0l,0.0l,1.0l,-1.0l);
+         Q44 = nifti_mat44_mul(patient_to_tal,Q44);
+         Q44 = nifti_mat44_mul(Q44,analyze_to_dicom);
+         //Q44 now equals 'mat' in spm_dicom_convert
+         //subasgn.m in SPM5 translates by one voxel...
+         LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,1.0l, 0.0l,1.0l,0.0l,1.0l, 0.0l,0.0l,1.0l,1.0l);
+         Q44 = nifti_mat44_mul(Q44,analyze_to_dicom);
+         #else */
+        for (int c=0; c<2; c++)
+            for (int r=0; r<4; r++)
+                Q44.m[c][r] = -Q44.m[c][r];
+        // #endif
+        mat33 Q;
+        LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1],
+                   d.orient[2],d.orient[5],d.CSA.sliceNormV[2],
+                   d.orient[3],d.orient[6],d.CSA.sliceNormV[3]);
+        if  (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this.
+            mat44 det;
+            sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly
+            LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l);
+            //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices;
+            Q44 = nifti_mat44_mul(Q44,det);
+        }
+    } else { //not a mosaic
+        sliceDir = verify_slice_dir(d, d2, h, &Q44);
+        for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient
+            for (int r=0; r<2; r++) //swap rows 1 & 2
+                Q44.m[r][c] = - Q44.m[r][c];
+#ifdef MY_DEBUG
+        reportMat44("final",Q44);
+#endif
+    }
+    setQSForm(h,Q44);
+    return sliceDir;
+} //headerDcm2NiiSForm()
+
+int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //final pass after de-mosaic
+    char txt[1024] = {""};
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder;
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29
+    sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime);
+    if (d.CSA.phaseEncodingDirectionPositive > 0) {
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";phase+=%d", d.CSA.phaseEncodingDirectionPositive);
+        strcat(txt,dtxt);
+    }
+    if ((d.CSA.bandwidthPerPixelPhaseEncode > 0) && ((d.phaseEncodingRC =='C') || (d.phaseEncodingRC =='R'))) {
+        float dwellTime = 0;
+        if (d.phaseEncodingRC =='C')
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+        else
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";dwell=%.3f", dwellTime);
+        strcat(txt,dtxt);
+    }
+    //printf(" Description= '%s'  [length=%lu]\n",txt, strlen(txt));
+    //x strlcpy(h->descrip,txt,80);
+    snprintf(h->descrip,80, "%s",txt);
+    if (strlen(d.imageComments) > 0)
+        snprintf(h->aux_file,24,"%s",d.imageComments);
+    return headerDcm2NiiSForm(d,d2, h);
+} //headerDcm2Nii2()
+
+int dcmStrLen (int len) {
+    if (len < kDICOMStr)
+        return len+1;
+    else
+        return kDICOMStr;
+} //dcmStrLen()
+
+struct TDICOMdata clear_dicom_data() {
+    struct TDICOMdata d;
+    
+    d.locationsInAcquisition = 0;
+    for (int i=0; i < 4; i++) {
+        for (int n=0; n < kMaxDTIv; n++)
+            d.CSA.dtiV[n][i] = 0;
+        d.patientPosition[i] = NAN;
+        //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D
+        d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D
+        d.stackOffcentre[i] = NAN;
+        d.angulation[i] = 0.0f;
+        d.xyzMM[i] = 1;
+    }
+    d.CSA.numDti = 0;
+    for (int i=0; i < 5; i++) d.xyzDim[i] = 1;
+        for (int i = 0; i < 7; i++) d.orient[i] = 0.0f;
+            d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false;
+            d.isHasPhase = false;
+            d.isHasMagnitude = false;
+            d.sliceOrient = kSliceOrientUnknown;
+            
+            //strcpy(d.sliceOrient,"\n");
+            strcpy(d.patientName, "John_Doe");
+            strcpy(d.patientID, "ID123");
+            strcpy(d.imageComments, "imgComments");
+            strcpy(d.studyDate, "1/1/1977");
+            strcpy(d.studyTime, "11:11:11");
+            d.dateTime = (double)19770703150928.0;
+            d.acquisitionTime = 0.0f;
+            strcpy(d.protocolName, "MPRAGE");
+            d.manufacturer = kMANUFACTURER_UNKNOWN;
+            d.isPlanarRGB = false;
+            d.lastScanLoc = NAN;
+            d.TR = 0;
+            d.TE = 0;
+            //d.locationsInAcquisition = 0;
+            d.numberOfDynamicScans = 0;
+            d.imageNum = 0;
+            d.intenScale = 1;
+            d.intenIntercept = 0;
+            d.seriesNum = 1;
+            d.acquNum = 0;
+            d.imageNum = 1;
+            d.imageStart = 0;
+            d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE
+            d.bitsAllocated = 16;//bits
+            d.bitsStored = 0;
+            d.samplesPerPixel = 1;
+            d.isValid = false;
+            d.isSigned = false; //default is unsigned!
+            d.isExplicitVR = true;
+            d.isLittleEndian = true; //DICOM initially always little endian
+            d.converted2NII = 0;
+            d.phaseEncodingRC = '?';
+            d.CSA.bandwidthPerPixelPhaseEncode = 0.0;
+            d.CSA.mosaicSlices = 0;
+            d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN;
+            d.CSA.protocolSliceNumber1 = 0;
+            d.CSA.phaseEncodingDirectionPositive = -1; //unknown
+            return d;
+} //clear_dicom_data()
+
+void dcmStrDigitsOnly(char* lStr) {
+    //e.g. change "H11" to " 11"
+    size_t len = strlen(lStr);
+    if (len < 1) return;
+    for (int i = 0; i < len; i++)
+        if (!isdigit(lStr[i]) )
+            lStr[i] = ' ';
+    
+}
+
+
+void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
+    //char test[] = " 1     2    3    ";
+    //lLength = (int)strlen(test);
+    if (lLength < 1) return;
+    char cString[lLength+1];
+    cString[lLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lLength);
+    //memcpy(cString, test, lLength);
+    for (int i = 0; i < lLength; i++)
+        if (cString[i]==' ') cString[i] = '_';
+    int len = 1;
+    for (int i = 1; i < lLength; i++) { //remove repeated "_"
+        if ((cString[i-1]!='_') || (cString[i]!='_')) {
+            cString[len] =cString[i];
+            len++;
+        }
+    } //for each item
+    if (cString[len-1] == '_') len--;
+    //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
+    cString[len] = 0; //null-terminate, strlcpy does this anyway
+    len = dcmStrLen(len);
+    //strlcpy(lOut,cString,len);
+    memcpy(lOut,cString,len-1);
+    lOut[len-1] = 0;
+    //printf(">>%s<<>>%s<<\n",cString,lOut);
+    //lOut[len-1]='z';
+    //printf(">>%s<<>>%s<<\n",cString,lOut);
+    //strlcpy(lOut,cString,len);
+} //dcmStr()
+
+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;
+    memcpy(&retVal, (char*)&lBuffer[0], 4);
+    if (!swap) return retVal;
+    char *floatToConvert = ( char* ) & lBuffer;
+    char *returnFloat = ( char* ) & retVal;
+    //swap the bytes into a temporary buffer
+    returnFloat[0] = floatToConvert[3];
+    returnFloat[1] = floatToConvert[2];
+    returnFloat[2] = floatToConvert[1];
+    returnFloat[3] = floatToConvert[0];
+    //printf("swapped val = %f\n",retVal);
+    return retVal;
+} //dcmFloat()
+
+int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer
+    if (littleEndian) {
+        if (lByteLength <= 3)
+            return  lBuffer[0] | (lBuffer[1]<<8); //shortint vs word?
+        return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word?
+    }
+    if (lByteLength <= 3)
+        return  lBuffer[1] | (lBuffer[0]<<8); //shortint vs word?
+    return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word?
+} //dcmInt()
+
+int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
+    char cString[lByteLength+1];
+    cString[lByteLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
+    return atoi(cString);
+} //dcmStrInt()
+
+int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
+    if (lByteLength < 2) return kMANUFACTURER_UNKNOWN;
+    char cString[lByteLength+1];
+    cString[lByteLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    //printf("MANU %s\n",cString);
+    if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I'))
+        return kMANUFACTURER_SIEMENS;
+    if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E'))
+        return kMANUFACTURER_GE;
+    if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H'))
+        return kMANUFACTURER_PHILIPS;
+    return kMANUFACTURER_UNKNOWN;
+} //dcmStrManufacturer
+
+typedef struct __attribute__((packed)) {
+    char name[64]; //null-terminated
+    int32_t vm;
+    char vr[4]; //  possibly nul-term string
+    int32_t syngodt;//  ??
+    int32_t nitems;// number of items in CSA
+    int32_t xx;// maybe == 77 or 205
+} TCSAtag; //Siemens csa tag structure
+typedef struct __attribute__((packed)) {
+    int32_t xx1, xx2_Len, xx3_77, xx4;
+} TCSAitem; //Siemens csa item structure
+
+float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) {
+    //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
+    //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion
+    TCSAitem itemCSA;
+    *ItemsOK = 0;
+    if (nItems < 1)  return 0.0f;
+    int lPos = 0;
+    for (int lI = 1; lI <= nItems; lI++) {
+        memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+        lPos +=sizeof(itemCSA);
+        if (itemCSA.xx2_Len > 0) {
+            char cString[itemCSA.xx2_Len];
+            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);
+            Floats[lI] = atof(cString);
+            *ItemsOK = lI; //some sequences have store empty items
+        }
+    } //for each item
+    return Floats[1];
+} //csaMultiFloat()
+
+int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, bool isVerbose) {
+    //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]);
+    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
+    int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24);
+    if (buff[lPos+4] != 77) return EXIT_FAILURE;
+    lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0
+    TCSAtag tagCSA;
+    TCSAitem itemCSA;
+    int itemsOK;
+    float lFloats[7];
+    for (int lT = 1; lT <= lnTag; lT++) {
+        memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag
+        lPos +=sizeof(tagCSA);
+        //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems);
+        if (tagCSA.nitems > 0) {
+            if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0)
+                CSA->mosaicSlices = round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            else if (strcmp(tagCSA.name, "B_value") == 0) {
+                CSA->dtiV[0][0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK);
+                CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag
+            }
+            else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){
+                CSA->dtiV[0][1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
+                CSA->dtiV[0][2] = lFloats[2];
+                CSA->dtiV[0][3] = lFloats[3];
+                if (isVerbose)
+                    printf("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]);
+            } 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)  ){
+                float sliceTimes[tagCSA.nitems+1];
+                csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK);
+                float minTimeIndex, minTimeValue, timeValue1;
+                CSA->multiBandFactor = 1;
+                timeValue1 = sliceTimes[1];
+                minTimeIndex = 1;
+                minTimeValue= sliceTimes[1];
+                for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time
+                    if (sliceTimes[z] < minTimeValue) {
+                        minTimeValue = sliceTimes[z];
+                        minTimeIndex = z;
+                    }
+                    if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++;
+                }
+                if (minTimeIndex == 2)
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2
+                else if (minTimeIndex == (itemsOK-1))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 4,3,2,1
+                else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_INC;
+                else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC;
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] < sliceTimes[itemsOK]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC;
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] > sliceTimes[itemsOK-2]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC;
+                else {
+                    /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems];
+                     for (int z = 1; z <= itemsOK; z++)
+                     [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");
+                }
+            } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0)
+                CSA->protocolSliceNumber1 = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0)
+                CSA->phaseEncodingDirectionPositive = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            /*if (strcmp(tagCSA.name, "SlicePosition_PCS") == 0) {
+             float f =  (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+             for (int k = 1; k <= tagCSA.nitems; k++)
+             printf("PCS %d = %f\n",k, lFloats[k]);
+             }
+             else {
+             printf("unused CSA tag %s with %d items\n",tagCSA.name, tagCSA.nitems);
+             }*/
+            for (int lI = 1; lI <= tagCSA.nitems; lI++) {
+                memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+                lPos +=sizeof(itemCSA);
+                lPos += ((itemCSA.xx2_Len +3)/4)*4;
+            }
+        } //if at least 1 item
+    }// for lT 1..lnTag
+    return EXIT_SUCCESS;
+} // readCSAImageHeader()
+
+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;
+    char cString[lByteLength+1];
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    cString[lByteLength] = 0; //null terminate
+    char *temp=( char *)malloc(lByteLength+1);
+    int f = 0,lStart = 0;
+    bool isOK = false;
+    for (int i = 0; i <= lByteLength; i++) {
+        if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true;
+        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);
+            if (f < lnFloats) {
+                f ++;
+                lFloats[f] = atof(temp);
+                //printf("%d == %f\n", f, atof(temp));
+            } //if f <= nFloats
+            lStart = i+1;
+        } //if isOK
+    }  //for i to length
+    free(temp);
+} //dcmMultiFloat()
+
+float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
+    char cString[lByteLength+1];
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    cString[lByteLength] = 0; //null terminate
+    return atof(cString);
+} //dcmStrFloat()
+
+/*
+ void dcmStrReport (int lByteLength, char lBuffer[]) {//print value at location
+ char cString[lByteLength+1];
+ memcpy(cString, (char*)&lBuffer[0], lByteLength);
+ cString[lByteLength] = 0; //null terminate
+ printf("%d dcmStrReport '%s'\n",lByteLength, cString);
+ } //dcmStrReport
+ */
+
+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);
+    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;
+    for (int i = 0; i < 10; i++) h->data_type[i] = 0;
+    for (int i = 0; i < 16; i++) h->intent_name[i] = 0;
+    if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3))
+        h->datatype = DT_RGB24;
+    else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1))
+        h->datatype = DT_UINT8;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned))
+        h->datatype = DT_INT16;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned))
+        h->datatype = DT_UINT16;
+    else if (d.bitsAllocated == 32)
+        h->datatype = DT_INT32;
+    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
+        
+        return EXIT_FAILURE;
+    }
+    if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16))
+        h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767
+    for (int i = 0; i < 8; i++) {
+        h->pixdim[i] = 0.0f;
+        h->dim[i] = 0;
+    }
+    h->regular = 114;
+    h->scl_inter = d.intenIntercept;
+    h->scl_slope = d.intenScale;
+    h->cal_max = 0;
+    h->cal_min = 0;
+    h->magic[0]='n';
+    h->magic[1]='+';
+    h->magic[2]='1';
+    h->magic[3]='\0';
+    h->vox_offset = d.imageStart;
+    h->bitpix = d.bitsAllocated * d.samplesPerPixel;
+    h->pixdim[1] = d.xyzMM[1];
+    h->pixdim[2] = d.xyzMM[2];
+    h->pixdim[3] = d.xyzMM[3];
+    h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec
+    h->dim[1] = d.xyzDim[1];
+    h->dim[2] = d.xyzDim[2];
+    h->dim[3] = d.xyzDim[3];
+    h->dim[4] = d.xyzDim[4];
+    if (h->dim[4] < 2)
+        h->dim[0] = 3;
+    else
+        h->dim[0] = 4;
+    
+    for (int i = 0; i <= 3; i++) {
+        h->srow_x[i] = 0.0f;
+        h->srow_y[i] = 0.0f;
+        h->srow_z[i] = 0.0f;
+    }
+    h->srow_x[0] = -1;
+    h->srow_y[2] = 1;
+    h->srow_z[1] = -1;
+    h->srow_x[3] =  (h->dim[1] /2);
+    h->srow_y[3] = -(h->dim[3] /2);
+    h->srow_z[3] =  (h->dim[2] /2);
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->toffset = 0;
+    h->intent_code = NIFTI_INTENT_NONE;
+    h->dim_info = 0; //Freq, Phase and Slice all unknown
+    h->xyzt_units = NIFTI_UNITS_UNKNOWN;
+    h->slice_duration = 0; //avoid +inf/-inf, NaN
+    h->intent_p1 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p2 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p3 = 0;  //avoid +inf/-inf, NaN
+    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);
+    return EXIT_SUCCESS;
+} // headerDcm2Nii()
+
+bool isFloatDiff (float a, float b) {
+    return (fabs (a - b) > FLT_EPSILON);
+} //isFloatDiff()
+
+ivec3 setVec3i(int x, int y, int z)
+{
+    ivec3 v = {x, y, z};
+    return v;
+} //setVec3i()
+
+mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) {
+    // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!]
+    mat33 ret;
+    for (int r=0; r<3; r++) {
+        for(int c=0; c<3; c++)
+            ret.m[r][c] = m.m[r][v.v[c]-1];
+    }
+    return ret;
+} //nifti_mat33_reorder_cols()
+
+/*bool isExt (char *file_name, const char* ext) {
+ char *p_extension;
+ if((p_extension = strrchr(file_name,'.')) != NULL )
+ if(strcmp(p_extension,ext) == 0) return true;
+ return false;
+ 
+ }*/
+
+void changeExt (char *file_name, const char* ext) {
+    char *p_extension;
+    p_extension = strrchr(file_name, '.');
+    if (p_extension)
+    {
+        strcpy(++p_extension, ext);
+    }
+} //changeExt()
+
+struct TDICOMdata  nii_readParRec (char * parname) {
+    struct TDICOMdata d = clear_dicom_data();
+    FILE *fp = fopen(parname, "r");
+    if (fp == NULL) return d;
+#define LINESZ 2048
+#define	kSlice	0
+#define	kEcho	1
+#define	kDyn	2
+#define	kCardiac	3
+#define	kImageType	4
+#define	kSequence	5
+#define	kIndex	6
+#define	kBitsPerVoxel	7
+#define	kXdim	9
+#define	kYdim	10
+#define	kRI	11
+#define	kRS	12
+#define	kSS	13
+#define	kAngulationAPs	16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z!
+#define	kAngulationFHs	17
+#define	kAngulationRLs	18
+#define	kPositionAP	19
+#define	kPositionFH	20
+#define	kPositionRL	21
+#define	kThickmm	22
+#define	kGapmm	23
+#define kSliceOrients 25
+#define	kXmm	28
+#define	kYmm	29
+#define	kTEcho	30
+#define	kDynTime	31
+#define	kGradientNumber 42
+#define	kbval 33
+#define	kv1	47
+#define	kv2	45
+#define	kv3	46
+#define	kASL	48
+    char buff[LINESZ];
+    bool ADCwarning = false;
+    for (int n=0; kMaxDTIv < 4; n++)
+        d.CSA.dtiV[n][0] = -1; //set to impossible value to detect re-usage by ADC map
+    int parVers = 0;
+    int nCols = 26;
+    int slice = 0;
+    //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order
+    const int kMaxCols = 49;
+    float *cols = (float *)malloc(sizeof(float) * kMaxCols);
+    char *p = fgets (buff, LINESZ, fp);
+    bool isIntenScaleVaries = false;
+    bool isIndexSequential = true;
+    while (p) {
+        if (strlen(buff) < 1)
+            continue;
+        if (buff[0] == '#') { //comment
+            char Comment[7][50];
+            sscanf(buff, "# %s %s\n", Comment[0], Comment[1]);
+            if (strcmp(Comment[1], "TRYOUT") == 0) {
+                sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3]
+                       ,Comment[4], Comment[5],Comment[6]);
+                parVers = (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
+                    return d;
+                    //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns
+                } else if (parVers < 41)
+                    nCols = 32; //e.g PAR 4.0
+                else if (parVers < 42)
+                    nCols = 47; //e.g. PAR 4.1
+                else
+                    nCols = kMaxCols; //e.g. PAR 4.2
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '#' comment
+        if (buff[0] == '.') { //tag
+            char Comment[8][50];
+            sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]);
+            
+            if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) {
+                d.acquNum = atoi( Comment[3]);
+                d.seriesNum = d.acquNum;
+            }
+            if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0))
+                d.TR = atof(Comment[4]);
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.patientName, Comment[3]);
+                strcat(d.patientName, Comment[4]);
+                strcat(d.patientName, Comment[5]);
+                strcat(d.patientName, Comment[6]);
+                strcat(d.patientName, Comment[7]);
+                //printf("%s\n",d.patientName);
+                
+            }
+            if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.protocolName, Comment[3]);
+                strcat(d.protocolName, Comment[4]);
+                strcat(d.protocolName, Comment[5]);
+                strcat(d.protocolName, Comment[6]);
+                strcat(d.protocolName, Comment[7]);
+                //printf("%s\n",d.protocolName);
+            }
+            if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
+                strcpy(d.studyDate, Comment[3]);
+                strcpy(d.studyTime, Comment[5]);
+                //to do convert to traditional DICOM style date time
+            }
+            if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.stackOffcentre[2] = atof(Comment[5]);
+                d.stackOffcentre[3] = atof(Comment[6]);
+                d.stackOffcentre[1] = atof(Comment[7]);
+            }
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.patientOrient[0] = toupper(Comment[3][0]);
+                d.patientOrient[1] = toupper(Comment[4][0]);
+                d.patientOrient[2] = toupper(Comment[5][0]);
+                d.patientOrient[3] = 0;
+            }
+            if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) {
+                d.xyzDim[3] = atoi(Comment[5]);
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '.' tag
+        if (strlen(buff) < 24) { //empty line
+            p = fgets (buff, LINESZ, fp);//get next line
+            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
+            return d;
+        }
+        for (int i = 0; i < nCols; i++)
+            cols[i] = strtof(p, &p); // p+1 skip comma, read a float
+        if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3...
+        slice ++;
+        if (slice == 1) {
+            //for (int i = 0; i < nCols; i++)
+            //    cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices
+            d.xyzDim[1] = cols[kXdim];
+            d.xyzDim[2] = cols[kYdim];
+            d.xyzMM[1] = cols[kXmm];
+            d.xyzMM[2] = cols[kYmm];
+            d.xyzMM[3] = cols[kThickmm] + cols[kGapmm];
+            d.patientPosition[1] = cols[kPositionRL];
+            d.patientPosition[2] = cols[kPositionAP];
+            d.patientPosition[3] = cols[kPositionFH];
+            d.angulation[1] = cols[kAngulationRLs];
+            d.angulation[2] = cols[kAngulationAPs];
+            d.angulation[3] = cols[kAngulationFHs];
+            d.sliceOrient = cols[kSliceOrients];
+            d.TE = cols[kTEcho];
+            d.bitsAllocated = cols[kBitsPerVoxel];
+            d.bitsStored = cols[kBitsPerVoxel];
+            d.intenIntercept = cols[kRI];
+            d.intenScale = cols[kRS];
+        } 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
+                return d;
+            }
+            if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) ||
+                                                              (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) ||
+                                                              (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position
+                d.patientPositionSequentialRepeats = slice-1;
+            
+            if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI]))
+                isIntenScaleVaries = true;
+        }
+        if (cols[kImageType] == 0) d.isHasMagnitude = true;
+        if (cols[kImageType] != 0) d.isHasPhase = true;
+        if (cols[kGradientNumber] > 0) {
+            int dir = cols[kGradientNumber];
+            if ((cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) {
+                if (d.CSA.dtiV[dir-1][0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2)
+                //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug
+                //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof.
+                ADCwarning = true;
+            }
+            if (dir > d.CSA.numDti) d.CSA.numDti = dir;
+
+            if (cols[dir] <= kMaxDTIv) {
+                d.CSA.dtiV[dir-1][0] = cols[kbval];
+                d.CSA.dtiV[dir-1][1] = cols[kv1];
+                d.CSA.dtiV[dir-1][2] = cols[kv2];
+                d.CSA.dtiV[dir-1][3] = cols[kv3];
+
+            } //save DTI direction
+        } //if DTI directions
+        //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
+        p = fgets (buff, LINESZ, fp);//get next line
+    }
+    free (cols);
+    fclose (fp);
+    d.isValid = true;
+    d.isSigned = 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 << "DTI directions"<<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");
+    
+    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 DTI directions\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 = M_PI/180;
+            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);
+    mat33 R = nifti_mat33_mul( rx,ry );
+    R = nifti_mat33_mul( R,rz);
+    ivec3 ixyz = setVec3i(1,2,3);
+    if (d.sliceOrient == kSliceOrientSag) {
+        ixyz = setVec3i(2,3,1);
+        for (int r = 0; r < 3; r++)
+            for (int c = 0; c < 3; c++)
+                if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns
+    }else if (d.sliceOrient == kSliceOrientCor) {
+        ixyz = setVec3i(1,3,2);
+        for (int r = 0; r < 3; r++)
+            R.m[r][2] = -R.m[r][2]; //invert rows of final column
+    }
+    R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix
+    d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0];
+    d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1];
+    mat33 diag;
+    LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f,  0.0f,d.xyzMM[2],0.0f,  0.0f,0.0f, d.xyzMM[3]);
+    R= nifti_mat33_mul( R, diag );
+    mat44 R44;
+    LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1],
+               R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2],
+               R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]);
+    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;
+    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]);
+    eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye
+    R44= nifti_mat44_mul( R44 , eye );
+    vec4 y;
+    y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=d.xyzDim[3]-1; y.v[3]=1.0f;
+    y= nifti_vect44mat44_mul(y, R44 );
+    int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k)
+    if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i)
+        if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j)
+            if  (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) {
+                d.patientPosition[1] = R44.m[0][3];
+                d.patientPosition[2] = R44.m[1][3];
+                d.patientPosition[3] = R44.m[2][3];
+                d.patientPositionLast[1] = y.v[0];
+                d.patientPositionLast[2] = y.v[1];
+                d.patientPositionLast[3] = y.v[2];
+            }else {
+                //d.patientPosition
+                d.patientPosition[1] = y.v[0];
+                d.patientPosition[2] = y.v[1];
+                d.patientPosition[3] = y.v[2];
+                d.patientPositionLast[1] = R44.m[0][3];
+                d.patientPositionLast[2] = R44.m[1][3];
+                d.patientPositionLast[3] = R44.m[2][3];
+            }
+    //finish up
+    changeExt (parname, "REC");
+    d.locationsInAcquisition = d.xyzDim[3];
+    d.manufacturer = kMANUFACTURER_PHILIPS;
+    d.imageStart = 0;
+    return d;
+} //nii_readParRec()
+
+size_t nii_ImgBytes(struct nifti_1_header hdr) {
+    //unsigned long imgsz = nii_ImgBytes(hdr);
+    size_t imgsz = hdr.bitpix/8;
+    for (int i = 1; i < 8; i++)
+        if (hdr.dim[i]  > 1)
+            imgsz = imgsz * hdr.dim[i];
+    return imgsz;
+} //nii_ImgBytes()
+
+unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) {
+    //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html
+    if (nMosaicSlices < 2) return inImg;
+    //Byte inImg[ [img length] ];
+    //[img getBytes:&inImg length:[img length]];
+    int nRowCol = ceil(sqrt(nMosaicSlices));
+    int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8;
+    int lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8;
+    int col = 0;
+    int row = 0;
+    int lOutPos = 0;
+    hdr->dim[1] = hdr->dim[1]/nRowCol;
+    hdr->dim[2] = hdr->dim[2]/nRowCol;
+    hdr->dim[3] = nMosaicSlices;
+    size_t imgsz = nii_ImgBytes(*hdr);
+    unsigned char *outImg = (unsigned char *)malloc(imgsz);
+    for (int m=1; m <= nMosaicSlices; m++) {
+        int lPos = (row * rowBytes) + (col * colBytes);
+        for (int y = 0; y < hdr->dim[2]; y++) {
+            memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes
+            lPos += lineBytes;
+            lOutPos +=colBytes;
+        }
+        col ++;
+        if (col >= nRowCol) {
+            row ++;
+            col = 0;
+        } //start new column
+    } //for m = each mosaic slice
+    if (ProtocolSliceNumber1 > 1) {
+#ifdef myUseCOut
+     	std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl;
+#else
+        printf("WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED\n");
+#endif
+    }
+    /*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;
+     memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes
+     int lOutPos = sliceBytes * (hdr->dim[3]-1);
+     int lPos = 0;
+     for (int m=0; m < nMosaicSlices; m++) {
+     memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes);
+     lPos += sliceBytes;
+     lOutPos -= sliceBytes;
+     }
+     }*/
+    free(inImg);
+    return outImg;
+    //return [NSData dataWithBytes:&outImg length:outlen];
+} //nii_demosaic()
+
+unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    size_t lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24)) {
+        lineBytes = hdr->dim[1];
+        dim3to7 = dim3to7 * 3;
+    } //rgb data saved planar (RRR..RGGGG..GBBB..B
+    
+    //printf("smoking gun %d %d\n",hdr->dim[3],hdr->dim[4]); return bImg;
+    
+    unsigned char line[lineBytes];
+    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
+        size_t slBottom = (size_t)sl*sliceBytes;
+        size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes;
+        for (int y = 0; y < halfY; y++) {
+            //swap order of lines
+            memcpy(&line, &bImg[slBottom], lineBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], lineBytes);
+            memcpy(&bImg[slTop], &line, lineBytes);
+            slTop -= lineBytes;
+            slBottom += lineBytes;
+        } //for y
+    } //for each slice
+    return bImg;
+} //nii_flipImgY()
+
+unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
+    if (halfZ < 1) return bImg;
+    int dim4to7 = 1;
+    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];
+    unsigned char slice[sliceBytes];
+    
+    for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice
+        long long slBottom = vol*volBytes;
+        long long slTop = ((vol+1)*volBytes)-sliceBytes;
+        for (int z = 0; z < halfZ; z++) {
+            //swap order of lines
+            memcpy(&slice, &bImg[slBottom], sliceBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes);
+            memcpy(&bImg[slTop], &slice, sliceBytes);
+            slTop -= sliceBytes;
+            slBottom += sliceBytes;
+        } //for Z
+    } //for each volume
+    return bImg;
+} //nii_flipImgZ()
+
+unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
+    //flip slice order
+    if (h->dim[3] < 2) return bImg;
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0,0,h->dim[3]-1);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipZ;
+    LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f);
+    s= nifti_mat33_mul( s , mFlipZ );
+    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);
+    return nii_flipImgZ(bImg,h);
+}//nii_flipZ()
+
+
+unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0,h->dim[2]-1,0);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipY;
+    LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f);
+    
+    s= nifti_mat33_mul( s , mFlipY );
+    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);
+    return nii_flipImgY(bImg,h);
+}//nii_flipY()
+
+unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr) {
+    size_t imgsz = nii_ImgBytes(hdr);
+    FILE *file = fopen(imgname , "rb");
+	if (!file) {
+         printf("Error: unable to open %s\n", imgname);
+         return NULL;
+    }
+	fseek(file, 0, SEEK_END);
+	long long fileLen=ftell(file);
+    if (fileLen < (imgsz+hdr.vox_offset)) return NULL;
+	fseek(file, hdr.vox_offset, SEEK_SET);
+    unsigned char *bImg = (unsigned char *)malloc(imgsz);
+	fread(bImg, imgsz, 1, file);
+	fclose(file);
+    return bImg;
+} //nii_loadImg()
+
+unsigned char * nii_rgb2Planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) {
+    //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+    if (hdr->datatype != DT_RGB24) return bImg;
+    if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr);
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
+    int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
+    unsigned char  slice24[ sliceBytes24 ];
+    int sliceOffsetR = 0;
+    for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+        memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
+        int sliceOffsetG = sliceOffsetR + sliceBytes8;
+        int sliceOffsetB = sliceOffsetR + 2*sliceBytes8;
+        int i = 0;
+        int j = 0;
+        for (int rgb = 0; rgb < sliceBytes8; rgb++) {
+            bImg[sliceOffsetR+j] =slice24[i];
+            i++;
+            bImg[sliceOffsetG+j] =slice24[i];
+            i++;
+            bImg[sliceOffsetB+j] =slice24[i];
+            i++;
+            j++;
+        }
+        sliceOffsetR += sliceBytes24;
+    } //for each slice
+    return bImg;
+} //nii_rgb2Planar()
+
+unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){
+    //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file
+    //WARNING: do this BEFORE nii_check16bitUnsigned!!!!
+    //if (hdr->datatype != DT_INT16) return img;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return img;
+    float * img32=(float*)malloc(nVox*sizeof(float));
+    if (hdr->datatype == DT_UINT8) {
+        uint8_t * img8i = (uint8_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img8i[i];
+    } else if (hdr->datatype == DT_UINT16) {
+        uint16_t * img16ui = (uint16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16ui[i];
+    } else if (hdr->datatype == DT_INT16) {
+        int16_t * img16i = (int16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16i[i];
+    } else if (hdr->datatype == DT_INT32) {
+        int32_t * img32i = (int32_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img32i[i];
+    }
+    free (img); //release previous image
+    for (int i=0; i < nVox; i++)
+        img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter;
+    hdr->scl_slope = 1;
+    hdr->scl_inter = 0;
+    hdr->datatype = DT_FLOAT;
+    hdr->bitpix = 32;
+    return (unsigned char*) img32;
+} //nii_iVaries()
+
+unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) {
+    //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension
+    int dim4to7 = 1;
+    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;
+#ifdef myUseCOut
+    std::cout<<"Converting XYTZ to XYZT with "<<hdr->dim[3]<<" slices (Z) and "<<  dim4to7<< "volumes" <<std::endl;
+    if ((dim4to7 % seqRepeats) != 0) {
+        std::cout<<"Error: patient position repeats "<<seqRepeats<<"  times, but this does not evenly divide number of volumes: "<<  dim4to7 <<std::endl;
+        seqRepeats = 1;
+    }
+    
+#else
+    printf("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)", seqRepeats,dim4to7);
+        seqRepeats = 1;
+    }
+    
+#endif
+    uint64_t typeRepeats = dim4to7 / seqRepeats;
+    uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8;
+    uint64_t seqBytes = sliceBytes * seqRepeats;
+    uint64_t typeBytes = seqBytes * hdr->dim[3];
+    
+    uint64_t imgSz = nii_ImgBytes(*hdr);
+    //this uses a lot of RAM, someday this could be done in place...
+    unsigned char *outImg = (unsigned char *)malloc(imgSz);
+    //memcpy(&tempImg[0], &bImg[0], imgSz);
+    uint64_t origPos = 0;
+    uint64_t Pos = 0; //
+    
+    for (int t = 0; t < typeRepeats; t++) { //for each volume
+        for (int s = 0; s < seqRepeats; s++) {
+            origPos = (t*typeBytes) +s*sliceBytes;
+            for (int z = 0; z < hdr->dim[3]; z++) { //for each slice
+                memcpy( &outImg[Pos],&bImg[origPos], sliceBytes);
+                Pos += sliceBytes;
+                origPos += seqBytes;
+            }
+        }//for s
+    }
+    free(bImg);
+    return outImg;
+} //nii_ImgBytes()
+
+unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){
+    if (hdr->bitpix < 9) return img;
+    uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8);
+    void *ar = (void*) img;
+    if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar );
+    if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar );
+    if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar );
+    return img;
+} //nii_byteswap()
+
+unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries) {
+    //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img
+
+    if (headerDcm2Nii(dcm, hdr) == EXIT_FAILURE) return NULL;
+    unsigned char * img = nii_loadImgCore(imgname, *hdr);
+    if (img == NULL) return img;
+#ifdef __BIG_ENDIAN__
+    if ((dcm.isLittleEndian) && (hdr->bitpix > 8))
+        img = nii_byteswap(img, hdr);
+#else
+    if ((!dcm.isLittleEndian) && (hdr->bitpix > 8))
+        img = nii_byteswap(img, hdr);
+#endif
+    if (hdr->datatype ==DT_RGB24) img = nii_rgb2Planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped
+    if (dcm.CSA.mosaicSlices > 1) {
+        img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, dcm.CSA.protocolSliceNumber1);
+        /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip
+         img = nii_flipImgY(img, hdr);
+         #endif*/
+    }
+    if (iVaries) img = nii_iVaries(img, hdr);
+    int nAcq = dcm.locationsInAcquisition;
+    if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) {
+        hdr->dim[4] = hdr->dim[3]/nAcq;
+        hdr->dim[3] = nAcq;
+        hdr->dim[0] = 4;
+    }
+    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);
+    return img;
+} //nii_loadImgX()
+
+struct TDICOMdata readDICOMv(char * fname, bool isVerbose) {
+    struct TDICOMdata d = clear_dicom_data();
+    strcpy(d.protocolName, ""); //fill dummy with empty space so we can detect kProtocolNameGE
+    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
+		
+		return d;
+	}
+	fseek(file, 0, SEEK_END);
+	long 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
+		return d;
+	}
+	fseek(file, 0, SEEK_SET);
+	//Allocate memory
+	unsigned char *buffer=(unsigned char *)malloc(fileLen+1);
+	if (!buffer) {
+		printf( "Memory error!");
+        fclose(file);
+		return d;
+	}
+	//Read file contents into buffer
+	fread(buffer, fileLen, 1, file);
+	fclose(file);
+    if ((buffer[128] != 'D') || (buffer[129] != 'I')  || (buffer[130] != 'C') || (buffer[131] != 'M')) {
+        free (buffer);
+        return d;
+    }
+    //DEFINE DICOM TAGS
+#define  kUnused 0x0001+(0x0001 << 16 )
+#define  kStart 0x0002+(0x0000 << 16 )
+#define  kTransferSyntax 0x0002+(0x0010 << 16)
+#define  kStudyDate 0x0008+(0x0020 << 16 )
+#define  kStudyTime 0x0008+(0x0030 << 16 )
+#define  kAcquisitionTime 0x0008+(0x0032 << 16 )
+#define  kManufacturer 0x0008+(0x0070 << 16 )
+#define  kProtocolNameGE 0x0008+(0x103E << 16 )
+#define  kComplexImageComponent 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent'
+#define  kPatientName 0x0010+(0x0010 << 16 )
+#define  kPatientID 0x0010+(0x0020 << 16 )
+#define  kMRAcquisitionType 0x0018+(0x0023 << 16)
+#define  kZThick  0x0018+(0x0050 << 16 )
+#define  kTR  0x0018+(0x0080 << 16 )
+#define  kTE  0x0018+(0x0081 << 16 )
+#define  kEchoNum  0x0018+(0x0086 << 16 ) //IS
+#define  kZSpacing  0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices'
+#define  kProtocolName  0x0018+(0x1030<< 16 )
+#define  kInPlanePhaseEncodingDirection  0x0018+(0x1312<< 16 ) //CS
+#define  kPatientOrient  0x0018+(0x5100<< 16 )    //0018,5100. patient orientation - 'HFS'
+    //#define  kDiffusionBFactorSiemens  0x0019+(0x100C<< 16 ) //   0019;000C;SIEMENS MR HEADER  ;B_value                         ;1;IS;1
+#define  kLastScanLoc  0x0019+(0x101B<< 16 )
+#define  kDiffusionDirectionGEX  0x0019+(0x10BB<< 16 ) //DS
+#define  kDiffusionDirectionGEY  0x0019+(0x10BC<< 16 ) //DS
+#define  kDiffusionDirectionGEZ  0x0019+(0x10BD<< 16 ) //DS
+#define  kPatientPosition 0x0020+(0x0032 << 16 )
+#define  kSeriesNum 0x0020+(0x0011 << 16 )
+#define  kAcquNum 0x0020+(0x0012 << 16 )
+#define  kImageNum 0x0020+(0x0013 << 16 )
+#define  kOrientation 0x0020+(0x0037 << 16 )
+#define  kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments'
+#define  kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE'
+#define  kSamplesPerPixel 0x0028+(0x0002 << 16 )
+#define  kPlanarRGB 0x0028+(0x0006 << 16 )
+#define  kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4
+#define  kDim2 0x0028+(0x0010 << 16 )
+#define  kDim1 0x0028+(0x0011 << 16 )
+#define  kXYSpacing  0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing'
+#define  kBitsAllocated 0x0028+(0x0100 << 16 )
+#define  kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored'
+#define  kIsSigned 0x0028+(0x0103 << 16 )
+#define  kIntercept 0x0028+(0x1052 << 16 )
+#define  kSlope 0x0028+(0x1053 << 16 )
+#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  kDiffusionBFactorGE  0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9
+#define  kCoilSiemens  0x0051+(0x100F << 16 )
+#define  kLocationsInAcquisition  0x0054+(0x0081 << 16 )
+#define  kIconImageSequence 0x0088+(0x0200 << 16 )
+#define  kDiffusionBFactor  0x2001+(0x1003 << 16 )// FL
+#define  kSliceOrient  0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
+#define  kLocationsInAcquisitionPhilips  0x2001+(0x1018 << 16 )
+#define  kNumberOfDynamicScans  0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans'
+#define  kMRAcquisitionTypePhilips 0x2005+(0x106F << 16)
+#define  kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP'
+#define  kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH'
+#define  kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL'
+#define  kMRStackOffcentreAP 0x2005+(0x1078 << 16)
+#define  kMRStackOffcentreFH 0x2005+(0x1079 << 16)
+#define  kMRStackOffcentreRL 0x2005+(0x107A << 16)
+#define  kDiffusionDirectionRL 0x2005+(0x10B0 << 16)
+#define  kDiffusionDirectionAP 0x2005+(0x10B1 << 16)
+#define  kDiffusionDirectionFH 0x2005+(0x10B2 << 16)
+#define  k2005140F 0x2005+(0x140F << 16)
+#define  kWaveformSq 0x5400+(0x0100 << 16)
+#define  kImageStart 0x7FE0+(0x0010 << 16 )
+#define  kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ
+#define  kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/
+#define  kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined]
+    double zSpacing = -1.0l; //includes slice thickness plus gap
+    int locationsInAcquisitionGE = 0; int locationsInAcquisitionPhilips = 0;
+    long lPos = 128+4; //4-byte signature starts at 128
+    uint32_t lLength;
+    uint32_t 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
+    char vr[2];
+    bool isIconImageSequence = false;
+    bool isSwitchToImplicitVR = false;
+    bool isSwitchToBigEndian = false;
+    //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2
+    bool is2005140FSQ = false; //for buggy Philips
+    bool is2005140FSQwarned = false; //for buggy Philips
+    bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice
+    int patientPositionCount = 0;
+    long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP)
+    long echoNum = 0;
+    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);
+        else
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToBigEndian = false;
+            d.isLittleEndian = false;
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        }//transfer syntax requests switching endian after group 0002
+        if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToImplicitVR = false;
+            d.isExplicitVR = false;
+        } //transfer syntax requests switching VR after group 0001
+        //uint32_t group = (groupElement & 0xFFFF);
+        lPos += 4;
+        if ((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) {
+            vr[0] = 'N';
+            vr[1] = 'A';
+            
+            //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail
+            //printf("xxx");
+            lLength = 4;
+        } else if (d.isExplicitVR) {
+            vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1];
+            if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos += 4;
+            } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W'))
+                       ) { //VR= UN, OB, OW, SQ  || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q'))
+                lPos = lPos + 4;  //skip 2 byte VR string and 2 reserved bytes = 4 bytes
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos = lPos + 4;  //skip 4 byte length
+            } else if   ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) {
+                lLength = 8; //Sequence Tag
+                is2005140FSQ = (groupElement == k2005140F);
+            } else { //explicit VR with 16-bit length
+                if ((d.isLittleEndian)  )
+                    lLength = buffer[lPos+2] | (buffer[lPos+3] << 8);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8);
+                lPos += 4;  //skip 2 byte VR string and 2 length bytes = 4 bytes
+            }
+        } else { //implicit VR
+            if (d.isLittleEndian)
+                lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+            else
+                lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+            lPos += 4;  //we have loaded the 32-bit length
+        } //if explicit else implicit VR
+        if (lLength == 0xFFFFFFFF) lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length
+        //next: look for required tags
+        if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions
+        switch ( groupElement ) {
+            case 	kTransferSyntax: {
+                char transferSyntax[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], transferSyntax);
+                //printf("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.2") == 0)
+                    isSwitchToBigEndian = true; //isExplicitVR=true;
+                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'\n",transferSyntax);
+#endif
+                    
+                    d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
+                }
+                break;} //{} provide scope for variable 'transferSyntax
+            case 	kStudyDate:
+                dcmStr (lLength, &buffer[lPos], d.studyDate);
+                break;
+            case 	kManufacturer:
+                d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]);
+                break;
+            case 	kComplexImageComponent:
+                d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H');
+                d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A');
+                break;
+            case 	kAcquisitionTime : {
+                char acquisitionTimeTxt[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt);
+                d.acquisitionTime = atof(acquisitionTimeTxt);
+                
+                break; }
+            case 	kStudyTime :
+                dcmStr (lLength, &buffer[lPos], d.studyTime);
+                break;
+            case 	kPatientName :
+                dcmStr (lLength, &buffer[lPos], d.patientName);
+                break;
+            case 	kPatientID :
+                dcmStr (lLength, &buffer[lPos], d.patientID);
+                break;
+            case 	kProtocolNameGE: {
+                if (d.manufacturer == kMANUFACTURER_GE)
+                    dcmStr (lLength, &buffer[lPos], d.protocolName);
+                break; }
+                
+            case 	kProtocolName : {
+                if (strlen(d.protocolName) < 1) //GE uses a generic session name here: do not overwrite kProtocolNameGE
+                    dcmStr (lLength, &buffer[lPos], d.protocolName);
+                break; }
+            case 	kPatientOrient :
+                dcmStr (lLength, &buffer[lPos], d.patientOrient);
+                break;
+            case 	kLastScanLoc :
+                d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+                /*case kDiffusionBFactorSiemens :
+                 if (d.manufacturer == kMANUFACTURER_SIEMENS)
+                 printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos]));
+                 
+                 break;*/
+            case kDiffusionDirectionGEX :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[0][1] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEY :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[0][2] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEZ :
+                if (d.manufacturer == kMANUFACTURER_GE) {
+                    d.CSA.dtiV[0][3] =  dcmStrFloat(lLength, &buffer[lPos]);
+                    d.CSA.numDti = 1;
+                }
+                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
+                    
+                    is2005140FSQwarned = true;
+                } else {
+                    patientPositionCount++;
+                    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
+                        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;
+                        } //if different position from 1st slice in file
+                    } //if not first slice in file
+                } //not after 2005,140F
+                break;
+            case 	kInPlanePhaseEncodingDirection:
+                d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol
+                break;
+            case 	kSeriesNum:
+                d.seriesNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kAcquNum:
+                d.acquNum = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kImageNum:
+                d.imageNum = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kPlanarRGB:
+                d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim3:
+                d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kSamplesPerPixel:
+                d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim2:
+                d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim1:
+                d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kXYSpacing:
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM);
+                
+                break;
+            case 	kImageComments:
+                dcmStr (lLength, &buffer[lPos], d.imageComments);
+                break;
+            case 	kLocationsInAcquisitionGE:
+                locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsAllocated :
+                d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsStored :
+                d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html
+                d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kTR :
+                d.TR = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kTE :
+                d.TE = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kEchoNum :
+                echoNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kZSpacing :
+                zSpacing = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kSlope :
+                d.intenScale = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kIntercept :
+                d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kZThick :
+                d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+                
+            case 	kCoilSiemens : {
+                if (d.manufacturer == kMANUFACTURER_SIEMENS) {
+                    //see if image from single coil "H12" or an array "HEA;HEP"
+                    char coilStr[kDICOMStr];
+                    dcmStr (lLength, &buffer[lPos], coilStr);
+                    //long coilNum = 0;
+                    char *ptr;
+                    dcmStrDigitsOnly(coilStr);
+                    coilNum = strtol(coilStr, &ptr, 10);
+                    if (*ptr != '\0')
+                        coilNum = 0;
+                }
+                break; }
+            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 	kNumberOfDynamicScans:
+                d.numberOfDynamicScans =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case	kMRAcquisitionType:
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                break;
+            case	kMRAcquisitionTypePhilips: //kMRAcquisitionType
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                break;
+            case	kAngulationRL:
+                d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationAP:
+                d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationFH:
+                d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreRL:
+                d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreAP:
+                d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreFH:
+                d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kSliceOrient: {
+                char orientStr[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], orientStr);
+                if (toupper(orientStr[0])== 'S')
+                    d.sliceOrient = kSliceOrientSag; //sagittal
+                else if (toupper(orientStr[0])== 'C')
+                    d.sliceOrient = kSliceOrientCor; //coronal
+                else
+                    d.sliceOrient = kSliceOrientTra; //transverse (axial)
+                break; }
+            case	kDiffusionBFactor:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions
+                    if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                        d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                }
+                break;
+            case    kDiffusionDirectionRL:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case kDiffusionDirectionAP:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kDiffusionDirectionFH:
+                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);
+                //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI
+                break;
+            case 	kWaveformSq:
+                d.imageStart = 1; //abort!!!
+#ifdef myUseCOut
+                std::cout<<"Warning: Unable to extract sound wave forms" <<std::endl;
+#else
+                printf("Warning: Unable to extract sound wave forms\n");
+#endif
+                break;
+            case 	kCSAImageHeaderInfo:
+                readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose);
+                break;
+                //case kObjectGraphics:
+                //    printf("---->%d,",lLength);
+                //    break;
+            case kDiffusionBFactorGE :
+                if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][0] =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            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
+                    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 	kOrientation :
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient);
+                break;
+            case 	kImageStart:
+                //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
+                if (!isIconImageSequence) //do not exit for proprietary thumbnails
+                    d.imageStart = (int)lPos;
+                //geiisBug = false;
+                isIconImageSequence = false;
+                break;
+        } //switch/case for groupElement
+        //printf("VR=%c%c tag=%04x,%04x length=%lu, pos=%ld x=%d\n",vr[0],vr[1],groupElement & 65535,groupElement>>16, lLength, lPos, d.xyzDim[1]);
+        lPos = lPos + (lLength);
+    }
+    free (buffer);
+    d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime);
+    //printf("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) && (d.locationsInAcquisition == 0))
+            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!
+                    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");
+                            d.xyzMM[2] = d.xyzMM[1];
+                        }
+    if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) {
+        printf("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");
+        d.xyzMM[3] = 1.0;
+    }
+    if (coilNum > 0) //segment images with multiple coils
+        d.seriesNum = d.seriesNum + (100*coilNum);
+        if (echoNum > 2) //segment images with multiple echoes
+            d.seriesNum = d.seriesNum + (100*echoNum);
+            if (isVerbose) {
+                printf("Patient Position %f %f %f\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
+                printf("DICOM 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\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);
+            }
+    return d;
+} // readDICOM()
+
+struct TDICOMdata readDICOM(char * fname) {
+    return readDICOMv(fname, false);
+}
+
+
diff --git a/qtGui/nii_dicom_batch.c b/qtGui/nii_dicom_batch.c
new file mode 100644
index 0000000..53991c3
--- /dev/null
+++ b/qtGui/nii_dicom_batch.c
@@ -0,0 +1,1475 @@
+//#define myNoSave //do not save images to disk
+
+#ifndef myDisableZLib
+ #include <zlib.h>
+ #ifndef myDisableTarGz 
+ // #include "untgz.h"
+ #endif
+#endif
+#ifdef myUseCOut
+ #include <iostream>
+#endif
+#include "nifti1_io_core.h"
+#include "nifti1.h"
+#include "nii_dicom_batch.h"
+#include "nii_dicom.h"
+#include "tinydir.h"
+#include <ctype.h> //toupper
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#include "nii_ortho.h"
+#if defined(_WIN64) || defined(_WIN32)
+#include <windows.h> //write to registry
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//gcc -O3 -o main main.c nii_dicom.c
+#if defined(_WIN64) || defined(_WIN32)
+const char kPathSeparator ='\\';
+const char kFileSep[2] = "\\";
+#else
+const char kPathSeparator ='/';
+const char kFileSep[2] = "/";
+#endif
+
+struct TDCMsort {
+    uint64_t indx, img;
+};
+
+struct TSearchList {
+    unsigned long numItems, maxItems;
+    char **str;
+};
+
+void dropFilenameFromPath(char *path) { //
+   const char *dirPath = strrchr(path, '/'); //UNIX
+   if (dirPath == 0)
+      dirPath = strrchr(path, '\\'); //Windows
+    if (dirPath == NULL) {
+        strcpy(path,"");
+    } else
+        path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory
+}
+
+void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2'
+{
+    const char *filename = strrchr(path, '/'); //UNIX
+    if (filename == 0)
+       filename = strrchr(path, '\\'); //Windows
+    //const char *filename = strrchr(path, kPathSeparator); //x
+    if (filename == NULL) {//no path separator
+        strcpy(pathParent,path);
+        return;
+    }
+    filename++;
+    strcpy(pathParent,filename);
+}
+
+bool is_fileexists(const char * filename) {
+    FILE * fp = NULL;
+    if ((fp = fopen(filename, "r"))) {
+        fclose(fp);
+        return true;
+    }
+    return false;
+}
+
+bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include <sys/stat.h>
+    struct stat buf;
+    stat(path, &buf);
+    return S_ISREG(buf.st_mode);
+} //is_file()
+
+bool is_exe(const char* path) { //requires #include <sys/stat.h>
+    struct stat buf;
+    stat(path, &buf);
+    return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) );
+} //is_exe()
+
+int is_dir(const char *pathname, int follow_link) {
+struct stat s;
+if ((NULL == pathname) || (0 == strlen(pathname)))
+	return 0;
+int err = stat(pathname, &s);
+if(-1 == err) {
+        return 0; /* does not exist */
+} else {
+    if(S_ISDIR(s.st_mode)) {
+       return 1; /* it's a dir */
+    } else {
+        return 0;/* exists but is no dir */
+    }
+}
+} //is_dir
+/*int is_dir(const char *pathname, int follow_link) {
+    //http://sources.gentoo.org/cgi-bin/viewvc.cgi/path-sandbox/trunk/libsbutil/get_tmp_dir.c?revision=260
+	struct stat buf;
+	int retval;
+	if ((NULL == pathname) || (0 == strlen(pathname)))
+		return 0;
+	retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+	if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
+		return 1;
+	if ((-1 == retval) && (ENOENT != errno)) // Some or other error occurred
+		return -1;
+	return 0;
+} //is_dir()
+*/
+
+bool isDICOMfile(const char * fname) {
+    FILE *fp = fopen(fname, "rb");
+	if (!fp)  return false;
+	fseek(fp, 0, SEEK_END);
+	long long fileLen=ftell(fp);
+    if (fileLen < 256) return false;
+	fseek(fp, 0, SEEK_SET);
+	unsigned char buffer[256];
+	fread(buffer, 256, 1, fp);
+	fclose(fp);
+    if ((buffer[128] != 'D') || (buffer[129] != 'I')  || (buffer[130] != 'C') || (buffer[131] != 'M'))
+        return false;
+    return true;
+} //isDICOMfile()
+
+void geCorrectBvecs(struct TDICOMdata *d, int sliceDir){
+    //0018,1312 phase encoding is either in row or column direction
+    //0043,1039 (or 0043,a039). b value (as the first number in the string).
+    //0019,10bb (or 0019,a0bb). phase diffusion direction
+    //0019,10bc (or 0019,a0bc). frequency diffusion direction
+    //0019,10bd (or 0019,a0bd). slice diffusion direction
+    //These directions are relative to freq,phase,slice, so although no
+    //transformations are required, you need to check the direction of the
+    //phase encoding. This is in DICOM message 0018,1312. If this has value
+    //COL then if swap the x and y value and reverse the sign on the z value.
+    //If the phase encoding is not COL, then just reverse the sign on the x value.
+    if (d->manufacturer != kMANUFACTURER_GE) return;
+    if (d->CSA.numDti < 1) return;
+    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;
+    }
+    bool col = false;
+    if (d->phaseEncodingRC== 'C')
+        col = true;
+    else if (d->phaseEncodingRC!= 'R') {
+        #ifdef myUseCOut
+     	std::cout<<"Error: Unable to determine DTI gradients, 0018,1312 should be either R or C" <<std::endl;
+    	#else
+        printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C");
+        #endif
+        return;
+    }
+    if (abs(sliceDir) != 3)
+            #ifdef myUseCOut
+     	std::cout<<"GE DTI gradients only tested for axial acquisitions" <<std::endl;
+    	#else
+        printf("GE DTI gradients only tested for axial acquisitions");
+        #endif
+    //printf("GE row(0) or column(1) = %d",col);
+    #ifdef myUseCOut
+    std::cout<<"Reorienting "<< d->CSA.numDti << "GE DTI gradients. Please validate if you are conducting DTI analyses. isCol="<<col <<std::endl;
+    #else
+    printf("Reorienting %d GE DTI gradients. Please validate if you are conducting DTI analyses. isCol=%d\n", d->CSA.numDti, col);
+    #endif
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }
+        d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][1];
+        if (!col) { //rows need to be swizzled
+            float swap = d->CSA.dtiV[i][1];
+            d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][2];
+            d->CSA.dtiV[i][2] = swap;
+        }
+        if (sliceDir < 0)
+                d->CSA.dtiV[i][3] = -d->CSA.dtiV[i][3];
+        if (isSameFloat(d->CSA.dtiV[i][1],-0)) d->CSA.dtiV[i][1] = 0.0f;
+        if (isSameFloat(d->CSA.dtiV[i][2],-0)) d->CSA.dtiV[i][2] = 0.0f;
+        if (isSameFloat(d->CSA.dtiV[i][3],-0)) d->CSA.dtiV[i][3] = 0.0f;
+        
+    }
+} //geCorrectBvecs()
+
+/*void philipsCorrectBvecs(struct TDICOMdata *d){
+    //Philips DICOM data stored in patient (LPH) space, regardless of settings in Philips user interface
+    //algorithm from PARtoNRRD/CATNAP (with July 20, 2007 patch) http://godzilla.kennedykrieger.org/~jfarrell/software_web.htm
+    //0018,5100. patient orientation - 'HFS'
+    //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
+    //2005,1071 MRStackAngulationAP
+    //2005,1072 MRStackAngulationFH
+    //2005,1073 MRStackAngulationRL
+    if (d->manufacturer != kMANUFACTURER_PHILIPS) return;
+    if (d->CSA.numDti < 1) return;
+    mat33 tpp,tpo;
+    if ((toupper(d->patientOrient[0])== 'F') && (toupper(d->patientOrient[1])== 'F'))
+        LOAD_MAT33(tpp, 0,-1,0, -1,0,0, 0,0,1); //feet first
+    else if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F'))
+        LOAD_MAT33(tpp, 0,1,0,-1,0,0, 0,0,-1); //head first
+    else {
+        printf("Unable to correct Philips DTI vectors: patient position must be head or feet first\n");
+        return;
+    }
+    //unused? mat33 rev_tpp =nifti_mat33_transpose(tpp);
+    if (toupper(d->patientOrient[2])== 'S')//supine
+        LOAD_MAT33 (tpo, 1,0,0, 0,1,0, 0,0,1);
+    else if (toupper(d->patientOrient[2])== 'P')//prone
+        LOAD_MAT33 (tpo,-1,0,0, 0,-1,0, 0,0,1);
+    else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'R'))   //DR
+        LOAD_MAT33 (tpo,0,-1,0, 1,0,0, 0,0,1);
+    else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'L'))//DL
+        LOAD_MAT33 (tpo,0,1,0, -1,0,0, 0,0,1);
+    else {
+        printf("DTI vector error: Position is not HFS,HFP,HFDR,HFDL,FFS,FFP,FFDR, or FFDL: %s\n",d->patientOrient);
+        return;
+    }
+    //unused? mat33 rev_tpo =nifti_mat33_transpose(tpo);
+    //unused? mat33 tpom = nifti_mat33_mul( tpo, tpp);
+    //unused? mat33 rev_tpom = nifti_mat33_mul( rev_tpp,rev_tpo  );
+    printf("Reorienting %d Philip DTI gradients with angulations %f %f %f. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti, d->angulation[1], d->angulation[2], d->angulation[3]);
+    
+    float rl = d->angulation[1]  * M_PI /180; //as radian
+    float ap = d->angulation[2]  * M_PI /180;
+    float fh = d->angulation[3]  * M_PI /180;
+    //printf(" %f %f %f \n",ap,fh,rl);
+    mat33 trl, tap, tfh, rev_tsom, dtiextra;
+    LOAD_MAT33 (trl,1,0,0,  0, cos(rl),- sin(rl),  0, sin(rl),cos(rl));
+    LOAD_MAT33 (tap, cos(ap),0, sin(ap),  0,1,0,                 - sin(ap),0, cos(ap));
+    LOAD_MAT33 (tfh,cos(fh),- sin(fh),0, sin(fh), cos(fh),0,    0,0,1);
+    mat33 rev_trl =nifti_mat33_transpose(trl);
+    mat33 rev_tap =nifti_mat33_transpose(tap);
+    mat33 rev_tfh =nifti_mat33_transpose(tfh);
+    mat33 mtemp1 = nifti_mat33_mul( trl, tap);
+    //unused? mat33 tang = nifti_mat33_mul( mtemp1, tfh);
+    mtemp1 = nifti_mat33_mul( rev_tfh, rev_tap );
+    mat33 rev_tang = nifti_mat33_mul( mtemp1, rev_trl);
+    //kSliceOrientSag
+    if (d->sliceOrient == kSliceOrientSag)//SAGITTAL
+        LOAD_MAT33 (rev_tsom, 0,0,1,  0,-1,0, -1,0,0 );
+    else if (d->sliceOrient == 2)//CORONAL
+        LOAD_MAT33 (rev_tsom, 0,0,1,  -1,0,0, 0,1,0 );
+    else
+        LOAD_MAT33 (rev_tsom, 0,-1,0,  -1,0,0, 0,0,1 );
+    LOAD_MAT33 (dtiextra, 0,-1,0,  -1,0,0, 0,0,1 );
+    mat33 mtemp2 = nifti_mat33_mul( dtiextra, rev_tsom);
+    mtemp1 = nifti_mat33_mul( mtemp2, rev_tang);
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        //printf("%d\tvin=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]);
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }
+        vec3 v3;
+        
+        for (int v= 1; v < 4; v++) //normalize and reverse vector directions
+            v3.v[v-1] =-d->CSA.dtiV[i][v]/vLen;
+        v3 = nifti_vect33mat33_mul(v3,mtemp1);
+        v3 = nifti_vect33_norm(v3);
+        d->CSA.dtiV[i][1] = v3.v[0];
+        d->CSA.dtiV[i][2] = v3.v[1]; //NIfTI Y reversed relative to DICOM
+        d->CSA.dtiV[i][3] = v3.v[2];
+        //printf("%d\tvoutt=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]);
+        
+    }
+    if ( d->sliceOrient != kSliceOrientTra)
+        printf("Warning: Philips DTI gradients only evaluated for axial (transverse) acquisitions. Please verify sign and direction\n");
+} //philipsCorrectBvecs()
+*/
+
+void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir){
+    //see Matthew Robson's  http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m
+    //convert DTI vectors from scanner coordinates to image frame of reference
+    //Uses 6 orient values from ImageOrientationPatient  (0020,0037)
+    // requires PatientPosition 0018,5100 is HFS (head first supine)
+    if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return;
+    if (d->CSA.numDti < 1) return;
+    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
+        return;
+    }
+    vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]);
+    vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]);
+    vec3 slice_vector = crossProduct(read_vector ,phase_vector);
+    read_vector = nifti_vect33_norm(read_vector);
+    phase_vector = nifti_vect33_norm(phase_vector);
+    slice_vector = nifti_vect33_norm(slice_vector);
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }//if bvalue=0
+        vec3 bvecs_old =setVec3(d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3]);
+        vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) );
+        bvecs_new = nifti_vect33_norm(bvecs_new);
+        d->CSA.dtiV[i][1] = bvecs_new.v[0];
+        d->CSA.dtiV[i][2] = -bvecs_new.v[1];
+        d->CSA.dtiV[i][3] = bvecs_new.v[2];
+        if (sliceDir == kSliceOrientMosaicNegativeDeterminant) d->CSA.dtiV[i][2] = -d->CSA.dtiV[i][2];
+        for (int v= 0; v < 4; v++)
+            if (d->CSA.dtiV[i][v] == -0.0f) d->CSA.dtiV[i][v] = 0.0f; //remove sign from values that are virtually zero
+    } //for each direction
+    #ifdef myUseCOut
+    if (sliceDir == kSliceOrientMosaicNegativeDeterminant)
+        std::cout<<"WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal)."<<std::endl;
+    else if ( d->sliceOrient == kSliceOrientTra)
+    	std::cout<<"Saving "<<d->CSA.numDti<<" DTI gradients. Please validate if you are conducting DTI analyses."<<std::endl;
+    else
+    	std::cout<<"WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files."<<std::endl;
+    #else
+    if (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");
+	#endif
+} //siemensPhilipsCorrectBvecs()
+
+bool isSamePosition (struct TDICOMdata d, struct TDICOMdata d2){
+    if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false;
+    if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false;
+    if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false;
+    return true;
+} //isSamePosition()
+
+bool nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir) {
+    //reports true if last volume is excluded (e.g. philip stores an ADC map)
+    //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) {//extract DTI from different slices
+        numDti = 0;
+        for (int i = 0; i < nConvert; i++) { //for each image
+            if ((dcmList[indx0].CSA.mosaicSlices > 1)  || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) {
+                if (numDti < kMaxDTIv) 
+                    for (int v = 0; v < 4; v++) //for each vector+B-value
+                        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;
+    }
+    if (numDti < 3) return false;
+    if (numDti > kMaxDTIv) {
+    	#ifdef myUseCOut
+    	std::cout<<"Error: more than "<<kMaxDTIv<<" DTI directions detected (check for a new software)"<<std::endl;
+		#else
+        printf("Error: more than %d DTI directions detected (check for a new software)", kMaxDTIv);
+        #endif
+        return false;
+    }
+    bool bValueVaries = false;
+    for (int i = 1; i < numDti; i++) //check if all bvalues match first volume
+        if (dcmList[indx0].CSA.dtiV[i][0] != dcmList[indx0].CSA.dtiV[0][0]) bValueVaries = true;
+    if (!bValueVaries) {
+        for (int i = 1; i < numDti; i++)
+                printf("bxyz %g %g %g %g\n",dcmList[indx0].CSA.dtiV[i][0],dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]);
+        printf("Error: only one B-value reported for all volumes: %g\n",dcmList[indx0].CSA.dtiV[0][0]);
+        return false;
+    }
+        
+    int firstB0 = -1;
+    for (int i = 0; i < numDti; i++) //check if all bvalues match first volume
+        if (isSameFloat(dcmList[indx0].CSA.dtiV[i][0],0) ) {
+            firstB0 = i;
+            break;
+        }
+    //printf("2015ALPHA %d -> %d\n",numDti, nConvert);
+    #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
+    bool isFinalADC = false;
+    /*if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)
+     && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f))
+     )
+     printf("xxx-->%f\n", dcmList[indx0].CSA.dtiV[numDti-1][3]);*/
+    if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)
+        && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f))  //not a B-0 image
+        && ((isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][1],0.0f)) ||
+            (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][2],0.0f)) ||
+            (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][3],0.0f)) )) {//yet all vectors are zero!!!! must be ADC
+            isFinalADC = true; //final volume is ADC map
+            numDti --; //remove final volume - it is a computed ADC map!
+            dcmList[indx0].CSA.numDti = numDti;
+        }
+    // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution
+    geCorrectBvecs(&dcmList[indx0],sliceDir);
+    siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir);
+    if (opts.isVerbose) {
+        for (int i = 0; i < (numDti-1); i++) {
+        	#ifdef myUseCOut
+    		std::cout<<i<<"\tB=\t"<<dcmList[indx0].CSA.dtiV[i][0]<<"\tVec=\t"<<
+                   dcmList[indx0].CSA.dtiV[i][1]<<"\t"<<dcmList[indx0].CSA.dtiV[i][2]
+                   <<"\t"<<dcmList[indx0].CSA.dtiV[i][3]<<std::endl;
+			#else
+            printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, dcmList[indx0].CSA.dtiV[i][0],
+                   dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]);
+            
+        	#endif
+        } //for each direction
+    }
+    if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction
+        for (int i = 0; i < (numDti-1); i++) {
+            if (fabs(dcmList[indx0].CSA.dtiV[i][2]) > FLT_EPSILON)
+                dcmList[indx0].CSA.dtiV[i][2] = -dcmList[indx0].CSA.dtiV[i][2];
+        } //for each direction
+    } //if not a mosaic
+    //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]);
+    char txtname[2048] = {""};
+    strcpy (txtname,pathoutname);
+    strcat (txtname,".bval");
+    //printf("Saving DTI %s\n",txtname);
+    FILE *fp = fopen(txtname, "w");
+    if (fp == NULL) return isFinalADC;
+    for (int i = 0; i < (numDti-1); i++)
+        fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][0]);
+    fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][0]);
+    fclose(fp);
+    strcpy(txtname,pathoutname);
+    strcat (txtname,".bvec");
+    //printf("Saving DTI %s\n",txtname);
+    fp = fopen(txtname, "w");
+    if (fp == NULL) return isFinalADC;
+    for (int v = 1; v < 4; v++) {
+        for (int i = 0; i < (numDti-1); i++)
+            fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][v]);
+        fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][v]);
+    }
+    fclose(fp);
+    return isFinalADC;
+} //nii_SaveDTI()
+
+float sqr(float v){
+    return v*v;
+}  //sqr()
+
+float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) {
+    //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices
+    return sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+
+                sqr(d1.patientPosition[2]-d2.patientPosition[2])+
+                sqr(d1.patientPosition[3]-d2.patientPosition[3]));
+} //intersliceDistance()
+
+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;
+    struct TDCMsort dcmSortIn[nConvert];
+    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);
+            dcmSort[k] = dcmSortIn[i];
+            i++;
+        }
+} //swapDim3Dim4()
+
+bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){
+    //detect whether some DICOM images report different intensity scaling
+    //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor.
+    // since NIfTI provides a single scaling factor for each file, these images require special consideration
+    if (nConvert < 2) return false;
+    bool iVaries = false;
+    float iScale = dcmList[dcmSort[0].indx].intenScale;
+    float iInter = dcmList[dcmSort[0].indx].intenIntercept;
+    for (int i = 1; i < nConvert; i++) { //stack additional images
+        uint64_t indx = dcmSort[i].indx;
+        if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true;
+        if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true;
+    }
+    return iVaries;
+} //intensityScaleVaries()
+
+
+/*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) {
+ //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+ //  see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard
+ if (hdr->datatype != DT_RGB24) return bImg;
+ int dim3to7 = 1;
+ for (int i = 3; i < 8; i++)
+ if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+ int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
+ int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
+ //Byte bImg[ bSz ];
+ //[img getBytes:&bImg length:bSz];
+ unsigned char slice24[sliceBytes24];
+ int sliceOffsetR = 0;
+ for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+ memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ } //for each slice
+ return bImg;
+ } //nii_ImgBytes()*/
+
+bool niiExists(const char*pathoutname) {
+    char niiname[2048] = {""};
+    strcat (niiname,pathoutname);
+    strcat (niiname,".nii");
+    if (is_fileexists(niiname)) return true;
+    char gzname[2048] = {""};
+    strcat (gzname,pathoutname);
+    strcat (gzname,".nii.gz");
+    if (is_fileexists(gzname)) return true;
+    return false;
+} //niiExists()
+
+int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) {
+    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) {
+            	#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
+                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
+            }
+        }
+     }
+    char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time
+    strcpy(inname, opts.filename);
+    char outname[1024] = {""};
+    char newstr[256];
+    if (strlen(inname) < 1) {
+        strcpy(inname, "T%t_N%n_S%s");
+    }
+    int start = 0;
+    int pos = 0;
+    while (pos<strlen(inname)) {
+        if (inname[pos] == '%') {
+            if (pos > start) {
+                strncpy(&newstr[0], &inname[0] + start, pos - start);
+                newstr[pos - start] = '\0';
+                strcat (outname,newstr);
+            }
+            pos++; //extra increment: skip both % and following character
+            char f = 'P';
+            if (pos<strlen(inname)) f = toupper(inname[pos]);
+            if (f == 'C')
+                strcat (outname,dcm.imageComments);
+            if (f == 'F')
+                strcat (outname,opts.indirParent);
+            if (f == 'I')
+                strcat (outname,dcm.patientID);
+            if (f == 'N')
+                strcat (outname,dcm.patientName);
+            if (f == 'P')
+                strcat (outname,dcm.protocolName);
+            if ((f >= '0') && (f <= '9')) {
+                if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
+                    char zeroPad[12] = {""};
+                    sprintf(zeroPad,"%%0%dd",atoi(&f));
+                    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);
+            }
+            if (f == 'T') {
+                sprintf(newstr, "%0.0f", dcm.dateTime);
+                strcat (outname,newstr);
+            }
+            start = pos + 1;
+        } //found a % character
+        pos++;
+    } //for each character in input
+    if (pos > start) { //append any trailing characters
+        strncpy(&newstr[0], &inname[0] + start, pos - start);
+        newstr[pos - start] = '\0';
+        strcat (outname,newstr);
+    }
+    if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName");
+    if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file
+    //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+    for (int pos = 0; pos<strlen(outname); pos ++)
+        if ((outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':')
+            || (outname[pos] == '"') || (outname[pos] == '\\') || (outname[pos] == '/')
+             
+            || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?'))
+            outname[pos] = '_';
+    //printf("name=*%s* %d %d\n", outname, pos,start);
+    char baseoutname[2048] = {""};
+    strcat (baseoutname,pth);
+    char appendChar[2] = {"a"};
+    appendChar[0] = kPathSeparator;
+    if (pth[strlen(pth)-1] != kPathSeparator)
+        strcat (baseoutname,appendChar);
+    strcat (baseoutname,outname);
+    char pathoutname[2048] = {""};
+    strcat (pathoutname,baseoutname);
+    int i = 0;
+    while (niiExists(pathoutname) && (i < 26)) {
+        strcpy(pathoutname,baseoutname);
+        appendChar[0] = 'a'+i;
+        strcat (pathoutname,appendChar);
+        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
+        return EXIT_FAILURE;
+    }
+    //printf("-->%s\n",pathoutname); return EXIT_SUCCESS;
+    //printf("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();
+    strcpy(opts.indirParent,"myFolder");
+    char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"};
+    nii_createFilename(dcm, niiFilenameBase, opts) ;
+    strcpy(niiFilename,"Example output filename: '");
+    strcat(niiFilename,niiFilenameBase);
+    if (opts.isGz)
+        strcat(niiFilename,".nii.gz'");        
+    else
+        strcat(niiFilename,".nii'");
+} //nii_createDummyFilename()
+
+#ifndef myDisableZLib
+void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src_buffer, unsigned long src_len) {
+    //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] = {""};
+    strcpy (fname,baseName);
+    strcat (fname,".nii.gz");
+    unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad
+    unsigned long cmp_len = compressBound(src_len+hdrPadBytes);
+    unsigned char *pCmp = (unsigned char *)malloc(cmp_len);
+    z_stream strm;
+    uLong file_crc32 = crc32(0L, Z_NULL, 0);
+    //strm.adler = crc32(0L, Z_NULL, 0);
+    strm.total_in = 0;
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    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) return;
+    //add header
+    unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes);
+    memcpy(pHdr,&hdr, sizeof(hdr));
+    strm.avail_in = (unsigned int)hdrPadBytes; // size of input
+    strm.next_in = (Bytef *)pHdr; // input header
+    deflate(&strm, Z_NO_FLUSH);
+    file_crc32 = crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes);
+    //add image
+    strm.avail_in = (unsigned int)src_len; // size of input
+    strm.next_in = (Bytef *)src_buffer; // input image
+    deflate(&strm, Z_FINISH); //Z_NO_FLUSH;
+    //finish up
+    deflateEnd(&strm);
+    file_crc32 = crc32(file_crc32, src_buffer, (unsigned int)src_len);
+    cmp_len = strm.total_out;
+    if (cmp_len <= 0) return;
+    FILE *fileGz = fopen(fname, "wb");
+    if (!fileGz) return;
+    //write header http://www.gzip.org/zlib/rfc-gzip.html
+    fputc((char)0x1f, fileGz); //ID1
+    fputc((char)0x8b, fileGz); //ID2
+    fputc((char)0x08, fileGz); //CM - use deflate compression method
+    fputc((char)0x00, fileGz); //FLG - no addition fields
+    fputc((char)0x00, fileGz); //MTIME0
+    fputc((char)0x00, fileGz); //MTIME1
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //XFL
+    fputc((char)0xff, fileGz); //OS
+    //write Z-compressed data
+    fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header and 4 bytes tail
+    //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order
+    fputc((unsigned char)(file_crc32), fileGz);
+    fputc((unsigned char)(file_crc32 >> 8), fileGz);
+    fputc((unsigned char)(file_crc32 >> 16), fileGz);
+    fputc((unsigned char)(file_crc32 >> 24), fileGz);
+    fputc((unsigned char)(strm.total_in), fileGz);
+    fputc((unsigned char)(strm.total_in >> 8), fileGz);
+    fputc((unsigned char)(strm.total_in >> 16), fileGz);
+    fputc((unsigned char)(strm.total_in >> 24), fileGz);
+    fclose(fileGz);
+    free(pCmp);
+} //writeNiiGz()
+#endif
+
+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);
+    #ifndef myDisableZLib
+        if  ((opts.isGz) &&  (strlen(opts.pigzname)  < 1) &&  ((imgsz+hdr.vox_offset) <  2147483647) ) { //use internal compressor
+        writeNiiGz (niiFilename, hdr,  im,imgsz);
+        return EXIT_SUCCESS;
+    }
+    #endif
+    char fname[2048] = {""};
+    strcpy (fname,niiFilename);
+    strcat (fname,".nii");
+    FILE *fp = fopen(fname, "wb");
+    if (!fp) return EXIT_FAILURE;
+    fwrite(&hdr, sizeof(hdr), 1, fp);
+    uint32_t pad = 0;
+    fwrite(&pad, sizeof( pad), 1, fp);
+    fwrite(&im[0], imgsz, 1, fp);
+    fclose(fp);
+    if ((opts.isGz) &&  (strlen(opts.pigzname)  > 0) ) {
+    	char command[768];
+    	strcpy(command, "\"" );
+        strcat(command, opts.pigzname );
+        strcat(command, "\" \"");
+        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)
+    	PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
+    	STARTUPINFO StartupInfo; //This is an [in] parameter
+    	ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+    	StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
+    	if(CreateProcess(NULL, command, NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL, NULL,&StartupInfo,&ProcessInfo)) { 
+        	WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+        	CloseHandle(ProcessInfo.hThread);
+        	CloseHandle(ProcessInfo.hProcess);
+    	} else
+    	#ifdef myUseCOut
+    	std::cout<<"compression failed "<<command<<std::endl;
+		#else
+		printf("compression failed %s\n",command);
+    	#endif
+    	#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
+    }
+    return EXIT_SUCCESS;
+} //nii_saveNII()
+
+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;
+    int nVol = 1;
+    for (int i = 4; i < 8; i++) {
+        if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i];
+        hdr1.dim[i] = 0;
+    }
+    hdr1.dim[0] = 3; //save as 3D file
+    size_t imgsz = nii_ImgBytes(hdr1);
+    size_t pos = 0;
+    char fname[2048] = {""};
+    char zeroPad[1024] = {""};
+    int zeroPadLen = (1 + log10(nVol));
+    sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen);
+    for (int i = 1; i <= nVol; i++) {
+        sprintf(fname,zeroPad,niiFilename,i);
+        if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE;
+        pos += imgsz;
+    }
+    return EXIT_SUCCESS;
+} //nii_saveNII3D
+
+
+
+void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){
+    //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required...
+    if (hdr->datatype != DT_UINT16) return;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return;
+    unsigned short * img16 = (unsigned short*) img;
+    unsigned short max16 = img16[0];
+    //clock_t start = clock();
+    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)
+            #ifdef myUseCOut
+    	std::cout<<"Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion"<<std::endl;
+		#else
+        printf("Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion\n");
+    	#endif
+    else
+        hdr->datatype = DT_INT16;
+} //nii_check16bitUnsigned()
+
+int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) {
+    //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image,
+    //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect
+
+    uint64_t indx0 = dcmSort[0].indx;
+    if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert;
+    float prevDx = 0.0;
+    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
+            return i;
+        }
+        prevDx = dx;
+    }
+    return nConvert; //all images in sequential order
+}
+
+
+int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts) {
+    bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList);
+    uint64_t indx = dcmSort[0].indx;
+    uint64_t indx0 = dcmSort[0].indx;
+    bool saveAs3D = dcmList[indx].isHasPhase;
+    struct nifti_1_header hdr0;
+    unsigned char * img = nii_loadImgX(nameList->str[indx], &hdr0,dcmList[indx], iVaries);
+    if (opts.isVerbose)
+    #ifdef myUseCOut
+    	std::cout<<"Converting "<<nameList->str[indx]<<std::endl;
+		#else
+        printf("Converting %s\n",nameList->str[indx]);
+        #endif
+    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);
+
+    
+    /*if ((nConvert < 2) && (dcmList[indx].locationsInAcquisition > 0)) { //stack philips 4D file
+        
+        int nAcq = dcmList[indx].locationsInAcquisition;
+        if ((hdr0.dim[0] < 4) && ((hdr0.dim[3]%nAcq)==0)) {
+            hdr0.dim[4] = hdr0.dim[3]/nAcq;
+            hdr0.dim[3] = nAcq;
+            hdr0.dim[0] = 4;
+        }
+        if (hdr0.dim[0] > 3) {
+            if (dcmList[indx].patientPositionSequentialRepeats > 1) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension)
+                imgM = nii_XYTZ_XYZT(imgM, &hdr0,dcmList[indx].patientPositionSequentialRepeats );
+        }
+    }*/
+    //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) {
+        if (hdr0.dim[3] < 2) {
+            //stack volumes with multiple acquisitions
+            int nAcq = 1;
+            //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq"
+            //  nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum);
+            //therefore, the 'same position' is the most robust solution in the real world.
+            if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) {
+                nConvert = siemensCtKludge(nConvert, dcmSort,dcmList);
+            }
+            if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition;
+            if (nAcq < 2 ) {
+                nAcq = 0;
+                for (int i = 0; i < nConvert; i++)
+                    if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++;
+            }
+            /*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);
+                //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) ) {
+                hdr0.dim[3] = nConvert/nAcq;
+                hdr0.dim[4] = nAcq;
+                hdr0.dim[0] = 4;
+            } else
+                hdr0.dim[3] = nConvert;
+            float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]);
+            if ((hdr0.dim[4] > 0) && (dx ==0) && (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)) {
+                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);
+            }
+            dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers:
+            // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm
+            if (dx > 0) hdr0.pixdim[3] = dx;
+        } else if (hdr0.dim[4] < 2) {
+            hdr0.dim[4] = nConvert;
+            hdr0.dim[0] = 4;
+        } else {
+            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]);
+        struct nifti_1_header hdrI;
+        for (int i = 1; i < nConvert; i++) { //stack additional images
+            indx = dcmSort[i].indx;
+            //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE;
+            img = nii_loadImgX(nameList->str[indx], &hdrI, dcmList[indx],iVaries);
+            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
+                return EXIT_FAILURE;
+            }
+            memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz);
+            free(img);
+        }
+    }
+
+    //printf("Mango %zd\n", (imgsz* nConvert)); return 0;
+    char pathoutname[2048] = {""};
+    if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) return EXIT_FAILURE;
+    if (strlen(pathoutname) <1) return EXIT_FAILURE;
+    int sliceDir = 0;
+    if (hdr0.dim[3] > 1)
+        sliceDir =headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
+    if (sliceDir < 0) {
+        imgM = nii_flipZ(imgM, &hdr0);
+        sliceDir = abs(sliceDir);
+    }
+    bool isFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir);
+    isFinalADC =isFinalADC; //simply to silence compiler warning when myNoSave defined
+
+    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
+    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
+    else
+        fflush(stdout); //GUI buffers printf, display all results
+    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));
+    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
+#ifndef myNoSave
+    if ((hdr0.dim[4] > 1) && (saveAs3D))
+        nii_saveNII3D(pathoutname, hdr0, imgM,opts);
+    else {
+        if ((isFinalADC) && (hdr0.dim[4] > 2)) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+            char pathoutnameADC[2048] = {""};
+            strcat(pathoutnameADC,pathoutname);
+            strcat(pathoutnameADC,"_ADC");
+            nii_saveNII(pathoutnameADC, hdr0, imgM, opts);
+            hdr0.dim[4] = hdr0.dim[4]-1;
+        };
+        nii_saveNII(pathoutname, hdr0, imgM, opts);
+    }
+#endif
+    free(imgM);
+    return EXIT_SUCCESS;
+} //saveDcm2Nii()
+
+int compareTDCMsort(void const *item1, void const *item2) {
+    //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html
+    struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1;
+    struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2;
+    if (dcm1->img < dcm2->img)
+        return -1;
+    else if (dcm1->img > dcm2->img)
+        return 1;
+    return 0; //tie
+} //compareTDCMsort()
+
+bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2) {
+    //returns true if d1 and d2 should be stacked together as a signle output
+    if (!d1.isValid) return false;
+    if (!d2.isValid) return false;
+    if ((d1.dateTime == d2.dateTime) && (d1.seriesNum == d2.seriesNum) && (d1.bitsAllocated == d2.bitsAllocated)
+        && (d1.xyzDim[1] == d2.xyzDim[1]) && (d1.xyzDim[2] == d2.xyzDim[2]) && (d1.xyzDim[3] == d2.xyzDim[3]) )
+        return true;
+    return false;
+} //isSameSet()
+
+void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) {
+    tinydir_dir dir;
+    tinydir_open(&dir, path);
+    while (dir.has_next) {
+        tinydir_file file;
+        tinydir_readfile(&dir, &file);
+        //printf("%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 (isDICOMfile(filename)) {
+            if (nameList->numItems < nameList->maxItems) {
+                nameList->str[nameList->numItems]  = (char *)malloc(strlen(filename)+1);
+                strcpy(nameList->str[nameList->numItems],filename);
+                //printf("OK\n");
+            }
+            nameList->numItems++;
+            //printf("dcm %lu %s \n",nameList->numItems, filename);
+        } else {
+        #ifdef MY_DEBUG
+            #ifdef myUseCOut
+                std::cout<<"Not a dicom"<< filename <<std::endl;
+            #else
+                printf("Not a dicom:\t%s\n", filename);
+            #endif
+        #endif
+        }
+        tinydir_next(&dir);
+    }
+    tinydir_close(&dir);
+} //searchDirForDICOM()
+
+int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    int nDuplicates = 0;
+    for (int i = 1; i < nConvert; i++) {
+        if (dcmSort[i].img == dcmSort[i-1].img)
+            nDuplicates ++;
+        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;
+} //removeDuplicates()
+
+int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    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"<<nameList->str[dcmSort[i].indx] <<std::endl;
+		#else
+            printf("\t%s\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
+            #endif
+            nDuplicates ++;
+        }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;
+} //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 )
+        if(strcicmp(p_extension,ext) == 0) return true;
+    //if(strcmp(p_extension,ext) == 0) return true;
+    return false;
+} //isExt()
+
+int convert_parRec(struct TDCMopts opts) {
+    //sample dataset from Ed Gronenschild <ed.gronenschild at maastrichtuniversity.nl>
+    struct TSearchList nameList;
+    nameList.numItems = 1;
+    nameList.maxItems = 1;
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
+    nameList.str[0]  = (char *)malloc(strlen(opts.indir)+1);
+    strcpy(nameList.str[0],opts.indir);
+    dcmList[0] = nii_readParRec(nameList.str[0]);
+    struct TDCMsort dcmSort[1];
+    dcmSort[0].indx = 0;
+    saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts);
+    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 > 0)
+        for (int i = 0; i < nameList.numItems; i++)
+            free(nameList.str[i]);
+    free(nameList.str);
+
+    return EXIT_SUCCESS;
+} //convert_parRec()
+
+int nii_loadDir (struct TDCMopts* opts) {
+    //printf("-->%s",opts->filename);
+    //return EXIT_FAILURE;
+    if (strlen(opts->indir) < 1) {
+         #ifdef myUseCOut
+    	std::cout<<"No input"<<std::endl;
+		#else
+        printf("No input\n");
+        #endif
+        return EXIT_FAILURE;
+    }
+    #ifdef myUseCOut
+     std::cout << "Version  " <<kDCMvers <<std::endl; 
+    #else
+    printf("Version %s\n",kDCMvers);
+    #endif
+
+    
+    char indir[512];
+    strcpy(indir,opts->indir);
+    bool isFile = is_fileNotDir(opts->indir);
+    if (isFile) {//if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom
+        dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir);
+    }
+    if (strlen(opts->outdir) < 1)
+        strcpy(opts->outdir,opts->indir);
+    else if (!is_dir(opts->outdir,true)) {
+        #ifdef myUseCOut
+    	std::cout << "Warning: output folder invalid "<< opts->outdir<<" will try %s\n"<< opts->indir <<std::endl;
+    	#else
+     	printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir);
+        #endif
+        strcpy(opts->outdir,opts->indir);
+    }
+    /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) {
+        #ifndef myDisableTarGz 
+         #ifndef myDisableZLib
+          untargz( indir, opts->outdir);
+         #endif
+        #endif
+    }*/
+    if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
+        strcpy(opts->indir, indir); //set to original file name, not path
+        return convert_parRec(*opts);
+    }
+
+    getFileName(opts->indirParent, opts->indir);
+    struct TSearchList nameList;
+    nameList.numItems = 0;
+    nameList.maxItems = 64000-1;
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file
+    //1: find filenames of dicom files
+    searchDirForDICOM(opts->indir, &nameList,  5,1);
+    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
+        free(nameList.str);
+        return EXIT_FAILURE;
+    }
+    if (nameList.numItems < 1) {
+                #ifdef myUseCOut
+    	std::cout << "Overwhelmed: found more than "<<nameList.maxItems<<" DICOM images in " << opts->indir <<std::endl;
+    	#else
+        printf("Overwhelmed: found more than %lu DICOM images in %s\n",nameList.maxItems, opts->indir);
+        #endif
+        //goto freeMem;
+        return EXIT_FAILURE;
+    }
+    long long nDcm = nameList.numItems;
+    #ifdef myUseCOut
+    	std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl;
+    	#else
+    printf( "Found %lu DICOM images\n", nameList.numItems);
+    #endif
+    // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
+    for (int i = 0; i < nameList.numItems; i++ )
+        dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose);
+    //3: stack DICOMs with the same Series
+    int nConvertTotal = 0;
+    for (int i = 0; i < nDcm; i++ ) {
+        if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) {
+            int nConvert = 0;
+            for (int j = i; j < nDcm; j++)
+                if (isSameSet(dcmList[i],dcmList[j]) )
+                    nConvert ++;
+            struct TDCMsort dcmSort[nConvert];
+            nConvert = 0;
+            for (int j = i; j < nDcm; j++)
+                if (isSameSet(dcmList[i],dcmList[j]) ) {
+                    dcmSort[nConvert].indx = j;
+                    dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32)+ dcmList[j].imageNum;
+                    dcmList[j].converted2NII = 1;
+                    nConvert ++;
+                }
+            qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers....
+            if (opts->isVerbose)
+                nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList);
+            else
+                nConvert = removeDuplicates(nConvert, dcmSort);
+            nConvertTotal += nConvert;
+            saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts);
+        }//convert all images of this series
+    }
+    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
+    if (nameList.numItems > 0)
+        for (int i = 0; i < nameList.numItems; i++)
+            free(nameList.str[i]);
+    free(nameList.str);
+    return EXIT_SUCCESS;
+} //nii_loadDir()
+
+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);
+		#else //myUseZLib
+ 		printf("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 
+    #else
+    strcpy(opts->pigzname,"/usr/local/bin/pigz");
+    char pigz[1024];
+    strcpy(pigz, opts->pigzname);
+    if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/bin/pigz");
+        if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron");
+        if (!is_exe(opts->pigzname)) {
+            strcpy(opts->pigzname,argv[0]);
+            dropFilenameFromPath(opts->pigzname);//, opts.pigzname);
+            char appendChar[2] = {"a"};
+            appendChar[0] = kPathSeparator;
+            if (opts->pigzname[strlen(opts->pigzname)-1] != kPathSeparator) strcat (opts->pigzname,appendChar);
+            strcat(opts->pigzname,"pigz_mricron");
+            #if defined(_WIN64) || defined(_WIN32)
+            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);
+            	#else //myUseZLib
+                printf("Compression will be faster with %s\n",pigz);
+            	#endif
+            #endif
+                strcpy(opts->pigzname,"");
+            } //no pigz_mricron in exe's folder
+        } //no /usr/local/pigz_mricron
+       }//no /usr/bin/pigz
+    } //no /usr/local/pigz
+    #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[]) {
+    readFindPigz(opts, argv);
+    strcpy(opts->indir,"");
+    strcpy(opts->outdir,"");
+    opts->isGz = false;
+    opts->isFlipY = true;
+#ifdef myDebug
+    opts->isVerbose =   true;
+#else
+    opts->isVerbose = false;
+#endif
+    strcpy(opts->filename,"%f_%p_%t_%s");
+     HKEY  hKey;
+    DWORD vSize     = 0;
+    DWORD dwDataType = 0;
+    DWORD dwValue    = 0;
+    //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle);
+    //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+    if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return;
+    }
+    vSize = sizeof(dwValue);
+    //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS)
+    if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast<BYTE *>(&dwValue), &vSize) == ERROR_SUCCESS)
+    	opts->isGz = dwValue;
+    vSize = 512;
+    char buffer[512];
+    if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS )
+ 	strcpy(opts->filename,buffer);
+ RegCloseKey(hKey); 
+} //readIniFile()
+
+#else
+//for Unix we will save preferences in a hidden text file in the home directory
+#define STATUSFILENAME "/.dcm2nii.ini"
+
+void readIniFile (struct TDCMopts *opts, const char * argv[]) {
+    readFindPigz(opts, argv);
+    sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME);
+    strcpy(opts->indir,"");
+    strcpy(opts->outdir,"");
+    opts->isGz = false;
+    opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
+#ifdef myDebug
+        opts->isVerbose =   true;
+#else
+        opts->isVerbose = false;
+#endif
+    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);
+        if ( strcmp(Setting,"isGZ") == 0 )
+            opts->isGz = atoi(Value);
+        else if ( strcmp(Setting,"filename") == 0 )
+            strcpy(opts->filename,Value);
+    }
+    fclose(fp);
+} //readIniFile()
+
+
+void saveIniFile (struct TDCMopts opts) {
+    FILE *fp = fopen(opts.optsname, "w");
+    //printf("%s\n",localfilename);
+    if (fp == NULL) return;
+    fprintf(fp, "isGZ=%d\n", opts.isGz);
+    fprintf(fp, "filename=%s\n", opts.filename);
+    fclose(fp);
+} //saveIniFile()
+
+#endif
+
+
+
+
diff --git a/qtGui/nii_ortho.c b/qtGui/nii_ortho.c
new file mode 100644
index 0000000..705a491
--- /dev/null
+++ b/qtGui/nii_ortho.c
@@ -0,0 +1,374 @@
+#include "nifti1.h"
+#include "nifti1_io_core.h"
+#include "nii_ortho.h"
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+#include <unistd.h>
+#include <stdio.h>
+//#define MY_DEBUG //verbose text reporting
+
+typedef struct  {
+    int v[3];
+} vec3i;
+
+mat33 matDotMul33 (mat33 a, mat33 b)
+// in Matlab: ret = a'.*b
+{
+    mat33 ret;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            ret.m[i][j] = a.m[i][j]*b.m[j][i];
+        }
+    }
+    return ret;
+}
+
+mat33 matMul33 (mat33 a, mat33 b)
+// mult = a * b
+{
+    mat33 mult;
+    for(int i=0;i<3;i++)
+    {
+        for(int j=0;j<3;j++)
+        {
+            mult.m[j][i]=0;
+            for(int k=0;k<3;k++)
+                mult.m[j][i]+=a.m[j][k]*b.m[k][i];
+        }
+    }
+    return mult;
+}
+
+float getOrthoResidual (mat33 orig, mat33 transform)
+{
+    mat33 mat = matDotMul33(orig, transform);
+    float ret = 0;
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            ret = ret + (mat.m[i][j]);
+        }
+    }
+    return ret;
+}
+
+mat33 getBestOrient(mat44 R, vec3i flipVec)
+//flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension
+{
+    mat33 ret, newmat, orig;
+    LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2],
+               R.m[1][0],R.m[1][1],R.m[1][2],
+               R.m[2][0],R.m[2][1],R.m[2][2]);
+    float best = 0;//FLT_MAX;
+    float newval;
+    for (int rot = 0; rot < 6; rot++) { //6 rotations
+        switch (rot) {
+            case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break;
+            case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break;
+            case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break;
+            case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break;
+            case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break;
+            case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break;
+        }
+        newval = getOrthoResidual(orig, newmat);
+        if (newval > best) {
+            best = newval;
+            ret = newmat;
+        }
+    }
+    return ret;
+}
+
+bool isMat44Canonical(mat44 R)
+//returns true if diagonals >0 and all others =0
+//  no rotation is necessary - already in perfect orthogonal alignment
+{
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if ((i == j) && (R.m[i][j] <= 0) ) return false;
+            if ((i != j) && (R.m[i][j] != 0) ) return false;
+            
+        }//j
+    }//i
+    return true;
+}
+
+vec3i setOrientVec(mat33 m)
+// Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution...
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    vec3i ret = {0, 0, 0};
+    //mat33 m = {-1,0,0, 0,1,0, 0,0,1};
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (m.m[i][j] > 0) ret.v[j] = i+1;
+            if (m.m[i][j] < 0) ret.v[j] = -(i+1);
+        }//j
+    }//i
+    return ret;
+}
+
+mat44 setMat44Vec(mat33 m33, vec3 Translations)
+//convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1
+{
+    mat44 m44;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            m44.m[i][j] = m33.m[i][j];
+        }
+    }
+    m44.m[0][3] = Translations.v[0];
+    m44.m[1][3] = Translations.v[1];
+    m44.m[2][3] = Translations.v[2];
+    m44.m[3][0] = 0;
+    m44.m[3][1] = 0;
+    m44.m[3][2] = 0;
+    m44.m[3][3] = 1;
+    return m44;
+}
+
+mat44 sFormMat(struct nifti_1_header *h) {
+    mat44 s;
+    s.m[0][0]=h->srow_x[0];
+    s.m[0][1]=h->srow_x[1];
+    s.m[0][2]=h->srow_x[2];
+    s.m[0][3]=h->srow_x[3];
+    s.m[1][0]=h->srow_y[0];
+    s.m[1][1]=h->srow_y[1];
+    s.m[1][2]=h->srow_y[2];
+    s.m[1][3]=h->srow_y[3];
+    s.m[2][0]=h->srow_z[0];
+    s.m[2][1]=h->srow_z[1];
+    s.m[2][2]=h->srow_z[2];
+    s.m[2][3]=h->srow_z[3];
+    s.m[3][0] = 0 ;
+    s.m[3][1] = 0 ;
+    s.m[3][2] = 0 ;
+    s.m[3][3] = 1 ;
+    return s;
+}
+
+void mat2sForm (struct nifti_1_header *h, mat44 s) {
+    h->srow_x[0] = s.m[0][0];
+    h->srow_x[1] = s.m[0][1];
+    h->srow_x[2] = s.m[0][2];
+    h->srow_x[3] = s.m[0][3];
+    h->srow_y[0] = s.m[1][0];
+    h->srow_y[1] = s.m[1][1];
+    h->srow_y[2] = s.m[1][2];
+    h->srow_y[3] = s.m[1][3];
+    h->srow_z[0] = s.m[2][0];
+    h->srow_z[1] = s.m[2][1];
+    h->srow_z[2] = s.m[2][2];
+    h->srow_z[3] = s.m[2][3];
+}
+
+size_t* orthoOffsetArray(int dim, int stepBytesPerVox) {
+    //return lookup table of length dim with values incremented by stepBytesPerVox
+    // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0
+    size_t *lut= (size_t *)malloc(dim*sizeof(size_t));
+    if (stepBytesPerVox > 0)
+        lut[0] = 0;
+    else
+        lut[0] = -stepBytesPerVox  *(dim-1);
+    if (dim > 1)
+        for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox;
+    return lut;
+} //orthoOffsetArray()
+
+//void  reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+void  reOrientImg( unsigned char *  img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+    //reslice data to new orientation
+    //generate look up tables
+    size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]);
+    size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]);
+    size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]);
+    //convert data
+    size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3]
+    size_t o = 0; //output address
+    uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time
+    uint8_t *outbuf = (uint8_t *) img; //source image
+    for (int vol= 0; vol < nvol; vol++) {
+        memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume
+        for (int z = 0; z < outDim.v[2]; z++)
+            for (int y = 0; y < outDim.v[1]; y++) 
+                for (int x = 0; x < outDim.v[0]; x++) {
+                    memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox);
+                    o = o+ bytePerVox;
+                } //for each x
+    } //for each volume
+    //free arrays
+    free(inbuf); 
+    free(xLUT);
+    free(yLUT); 
+    free(zLUT); 
+} //reOrientImg
+
+unsigned char *  reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM)
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    size_t nvox = h->dim[1] * h->dim[2] * h->dim[3];
+    if (nvox < 1) return img;
+    vec3i outDim= {0,0,0};
+    vec3i outInc= {0,0,0};
+    for (int i = 0; i < 3; i++) { //set dimension, pixdim and 
+        outDim.v[i] =  h->dim[abs(orientVec.v[i])];
+        if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1;
+        if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1];
+        if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2];
+        if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip
+    } //for each dimension
+    int nvol = 1; //convert all non-spatial volumes from source to destination
+    for (int vol = 4; vol < 8; vol++) {
+        if (h->dim[vol] > 1)
+            nvol = nvol * h->dim[vol];
+    }
+    reOrientImg(img, outDim, outInc, h->bitpix / 8,  nvol);
+    //now change the header....
+    vec3 outPix= {h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]};
+    for (int i = 0; i < 3; i++) {
+        h->dim[i+1] = outDim.v[i];
+        h->pixdim[i+1] = outPix.v[i];
+    }
+    mat44 s = sFormMat(h);
+    mat33 mat; //computer transform
+    LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2],
+                         s.m[1][0],s.m[1][1],s.m[1][2],
+                         s.m[2][0],s.m[2][1],s.m[2][2]);
+    mat = matMul33(  mat, orient);
+    s = setMat44Vec(mat, minMM); //add offset
+    mat2sForm(h,s);
+    h->qform_code = h->sform_code; //apply to the quaternion as well
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    return img;
+} //reOrient()
+
+float getDistance (vec3 v, vec3 min)
+//scalar distance between two 3D points - Pythagorean theorem
+{
+    return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2)  );
+}
+
+vec3 xyz2mm (mat44 R, vec3 v)
+{
+    vec3 ret;
+    for (int i = 0; i < 3; i++) {
+        ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] );
+    }
+    return ret;
+}
+
+vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec)
+//orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift
+// a simple solution is to first compute the most left, posterior, inferior voxel in the source image
+// this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix...
+{
+    int i,j, minIndex;
+    vec3i flipVecs[8];
+    vec3 corner[8], min;
+    mat44 s = sFormMat(h);
+    for (int i = 0; i < 8; i++) {
+        if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1;
+        if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1;
+        if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1;
+        corner[i] = setVec3(0,0,0); //assume no reflections
+        if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X
+        if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y
+        if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z
+        corner[i] = xyz2mm(s,corner[i]);
+    }
+    //find extreme edge from ALL corners....
+    min = corner[0];
+    for (i = 1; i < 8; i++) {
+        for (j = 0; j < 3; j++) {
+            if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j];
+        }
+    }
+    float dx; //observed distance from corner
+    float min_dx = getDistance (corner[0], min);
+    minIndex = 0; //index of corner closest to min
+    //see if any corner is closer to absmin than the first one...
+    for (i = 1; i < 8; i++) {
+        dx = getDistance (corner[i], min);
+        if (dx < min_dx) {
+            min_dx = dx;
+            minIndex = i;
+        }
+    }
+    min = corner[minIndex]; //this is the single corner closest to min from all
+    *flipVec= flipVecs[minIndex];
+    return min;
+}
+
+#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,
+           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]);
+}
+#endif
+
+unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
+    if ((h->dim[0] != 3) || (h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img;
+    if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided
+        mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d,
+                                         h->qoffset_x, h->qoffset_y, h->qoffset_z,
+                                         h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]);
+        mat2sForm(h,q); //convert q-form to s-form
+        h->sform_code = h->qform_code;
+    }
+    if (h->sform_code == NIFTI_XFORM_UNKNOWN) {
+         #ifdef MY_DEBUG
+         printf("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");
+        #endif
+        return img;
+    }
+    vec3i  flipV;
+    vec3 minMM = minCornerFlip(h, &flipV);
+    mat33 orient = getBestOrient(s, flipV);
+    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");
+        #endif
+        return img;
+    }
+    bool is24 = false;
+    if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices
+        return img;
+        is24 = true;
+        h->bitpix = 8;
+        h->dim[3] = h->dim[3] * 3;
+    }
+    img = reOrient(img, h,orientVec, orient, minMM);
+    if (is24 ) {
+        h->bitpix = 24;
+        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]);
+    reportMat44o("input",s);
+    s = sFormMat(h);
+    reportMat44o("output",s);
+    #endif
+    return img;
+}
+
+
+
diff --git a/qtGui/notes.txt b/qtGui/notes.txt
new file mode 100644
index 0000000..31465c5
--- /dev/null
+++ b/qtGui/notes.txt
@@ -0,0 +1 @@
+/Users/rorden/QT/5.2.1/clang_64/bin/macdeployqt /Users/rorden/dcm2/deploy/dcm2.app
\ No newline at end of file
diff --git a/rmfiles.command b/rmfiles.command
new file mode 100755
index 0000000..6df2e78
--- /dev/null
+++ b/rmfiles.command
@@ -0,0 +1,16 @@
+#!/bin/sh
+#remove duplicates
+cd /Users/rorden/Documents/cocoa/dcm2niix
+rm  ./qtGui/ni*.cpp
+rm  ./qtGui/ni*.h
+rm  ./qtGui/tinydir.h
+
+rm  ./wxWidgets/ni*.cpp
+rm  ./wxWidgets/ni*.h
+rm  ./wxWidgets/tinydir.h
+
+rm  ./xcode/dcm2/core/*.c
+rm  ./xcode/dcm2/core/*.cpp
+rm  ./xcode/dcm2/core/*.h
+rm  ./xcode/dcm2/core/tinydir.h
+
diff --git a/ucm.cmake b/ucm.cmake
new file mode 100644
index 0000000..32b407c
--- /dev/null
+++ b/ucm.cmake
@@ -0,0 +1,611 @@
+#
+# 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()
diff --git a/wxWidgets/clipboard.cpp b/wxWidgets/clipboard.cpp
new file mode 100644
index 0000000..f9949a9
--- /dev/null
+++ b/wxWidgets/clipboard.cpp
@@ -0,0 +1,282 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:   dcm.cpp
+// Purpose: wxWidgets wrapper for dcm2niix
+// Author:  Chris Rorden
+// Copyright: (c) Chris Rorden
+// Licence: BSD licence
+/////////////////////////////////////////////////////////////////////////////
+
+#include "wx/wxprec.h"
+#include <wx/stdpaths.h>
+#include "nii_dicom_batch.h"
+#include "wx/clipbrd.h"
+#include <iostream>
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWidgets headers)
+#ifndef WX_PRECOMP
+    #include "wx/wx.h"
+#endif
+
+#ifndef wxHAS_IMAGES_IN_RESOURCES
+    #include "../sample.xpm"
+#endif
+
+class MyApp : public wxApp
+{
+public:
+    virtual bool OnInit();
+    
+};
+
+class MyFrame : public wxFrame
+{
+public:
+    MyFrame(const wxString& title);
+    void OnQuit(wxCommandEvent&event);
+    void OnAbout(wxCommandEvent&event);
+    void OnSetOutputFolder(wxCommandEvent&event);
+    void OnDicom(wxCommandEvent&event);
+    void OnParRec(wxCommandEvent&event);
+    void OnCompressCheck(wxCommandEvent&event);
+    void OnCopy(wxCommandEvent&event);
+    void OnFileName(wxCommandEvent&event);
+    void OnShowPrefs();
+    void OnShowExampleFilename();
+    void OnDropFiles(wxDropFilesEvent&event);
+    void OnClose(wxCloseEvent& event);
+    //void OnProcessFile: (char * fname);
+    void OnProcessFile (char * fname);
+private:
+    wxTextCtrl        *m_textctrl;
+    wxCheckBox		*m_compressCheck;
+    wxTextCtrl        *m_fileName;
+    wxButton		*m_outputFolder;
+    struct TDCMopts opts;
+    bool applicationBusy;
+    DECLARE_EVENT_TABLE()
+};
+
+enum
+{
+    ID_Quit   = wxID_EXIT,
+    ID_About  = wxID_ABOUT,
+    ID_OutputFolder  = 100,
+    ID_Text   = 101,
+    ID_CompressCheck = 102,
+    ID_DicomMenu = 103,
+    ID_ParRecMenu = 104,
+    ID_CopyMenu = 105,
+    ID_FileName = 106
+};
+
+BEGIN_EVENT_TABLE(MyFrame, wxFrame)
+    EVT_MENU(ID_Quit,  MyFrame::OnQuit)
+    EVT_MENU(ID_About, MyFrame::OnAbout)
+    EVT_MENU(ID_DicomMenu, MyFrame::OnDicom)
+    EVT_MENU(ID_ParRecMenu, MyFrame::OnParRec)
+    EVT_MENU(ID_CopyMenu, MyFrame::OnCopy)
+    EVT_BUTTON(ID_OutputFolder, MyFrame::OnSetOutputFolder)
+    EVT_CHECKBOX(ID_CompressCheck, MyFrame::OnCompressCheck)
+    EVT_TEXT(ID_FileName, MyFrame::OnFileName)
+    EVT_CLOSE(MyFrame::OnClose)
+END_EVENT_TABLE()
+
+IMPLEMENT_APP(MyApp)
+
+
+void MyFrame::OnClose(wxCloseEvent& event) {
+    saveIniFile (opts); //save preferences
+    Destroy();
+}
+
+bool MyApp::OnInit() {
+    if ( !wxApp::OnInit() )
+        return false;
+	MyFrame *frame = new MyFrame("dcm2");
+    frame->SetClientSize(720, 440);
+	frame->SetMinSize(wxSize(640, 340));    
+    frame->Show(true);
+    return true;
+}
+
+MyFrame::MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title) {
+    // set the frame icon
+    this->applicationBusy = true;
+    SetIcon(wxICON(sample)); //&Open\tCtrl+O
+    wxMenu *fileMenu = new wxMenu;
+ 	fileMenu->Append(ID_DicomMenu, wxT("&DICOM to NIfTI...\tCtrl-D"));
+    fileMenu->Append(ID_ParRecMenu, wxT("&PAR/REC to NIfTI...\tCtrl-P"));
+    fileMenu->Append(ID_Quit, "E&xit\tAlt-X", "Quit this program");
+    wxMenu *editMenu = new wxMenu;
+    editMenu->Append(ID_CopyMenu, "&Copy\tCtrl-C", "Copy text to clipboard");
+    wxMenuBar *menuBar = new wxMenuBar();
+    #ifdef __WXMAC__
+		#define kTextLabelBorder 2
+		editMenu->Append(ID_About, "&About", "Show about dialog");
+	#else
+		#define kTextLabelBorder 6
+    	wxMenu *helpMenu = new wxMenu;
+    	helpMenu->Append(ID_About, "&About", "Show about dialog");
+    	menuBar->Append(helpMenu, "Help");
+	#endif
+	menuBar->Append(fileMenu, "File");
+    menuBar->Append(editMenu, " Edit");
+	SetMenuBar(menuBar);
+    wxPanel *panel = new wxPanel( this, -1 );
+    wxBoxSizer *main_sizer = new wxBoxSizer( wxVERTICAL );
+    wxBoxSizer *hbox1 = new wxBoxSizer(wxHORIZONTAL);
+	hbox1->Add( new wxStaticText( panel, wxID_ANY, "Compress" ), 0, wxEXPAND | wxTOP, kTextLabelBorder );
+	m_compressCheck = new wxCheckBox( panel, ID_CompressCheck, "" );
+	hbox1->Add( m_compressCheck, 0, wxBottom, 20);
+    hbox1->Add( new wxStaticText( panel, wxID_ANY, " Output name" ), 0, wxEXPAND| wxTOP, kTextLabelBorder );
+	m_fileName = new wxTextCtrl( panel, ID_FileName, "%f%s" );
+	hbox1->Add( m_fileName, 1, wxEXPAND, 5 );
+    hbox1->Add( new wxStaticText( panel, wxID_ANY, " Output folder" ), 0, wxEXPAND| wxTOP, kTextLabelBorder );
+    m_outputFolder = new wxButton( panel, ID_OutputFolder, " Input folder" );
+    hbox1->Add(m_outputFolder , 1, wxEXPAND, 0);
+    main_sizer->Add(hbox1, 0, wxEXPAND| wxALL, 4);
+    m_textctrl = new wxTextCtrl( panel, ID_Text, "", wxDefaultPosition,wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE );
+    main_sizer->Add( m_textctrl, 1, wxGROW );
+    panel->SetSizer( main_sizer );
+    wxString appPath = wxStandardPaths::Get().GetExecutablePath();
+    const char *appPathC = appPath.mb_str(wxConvUTF8);
+	readIniFile (&opts, &appPathC);
+	//redirect http://docs.wxwidgets.org/trunk/classwx_text_ctrl.html
+	//std::streambuf *sbOld = std::cout.rdbuf();
+	std::cout.rdbuf(m_textctrl);
+	//redirect until... std::cout.rdbuf(sbOld);
+	m_textctrl->DragAcceptFiles(true);
+	m_textctrl->Connect(wxEVT_DROP_FILES, wxDropFilesEventHandler(MyFrame::OnDropFiles), NULL, this);
+	this->OnShowPrefs();
+	
+	this->applicationBusy = false;
+}
+
+void MyFrame::OnProcessFile (char * fname)
+{
+	m_textctrl->Clear();
+    struct TDCMopts optsTemp;
+    optsTemp = opts; //conversion may change values like the outdir (if not specified)
+    strcpy(optsTemp.indir, fname);
+	clock_t start = clock();
+    nii_loadDir (&(optsTemp));
+    //printf("required %fms\n", ((double)(clock()-start))/1000);
+    std::cout << "required " <<((double)(clock()-start))/1000 <<"ms" <<std::endl; 
+    fflush(stdout); //GUI buffers printf, display all results
+}
+
+void MyFrame::OnDropFiles(wxDropFilesEvent& event) {
+	if (event.GetNumberOfFiles() < 1) return;
+	wxString* dropped = event.GetFiles();
+	wxString name =dropped[0];
+	char fname[1024];
+    strncpy(fname, (const char*)name.mb_str(wxConvUTF8), 1023);
+    OnProcessFile (fname);	
+}
+    
+void MyFrame::OnShowExampleFilename() {
+    char niiFilename[1024];
+    nii_createDummyFilename(niiFilename, opts);
+    m_textctrl->Clear();
+    wxString mystring = wxString::Format(wxT("%s\nVersion %s\n"),niiFilename,kDCMvers);
+    m_textctrl->AppendText(mystring);
+}
+
+void MyFrame::OnShowPrefs()
+{
+   applicationBusy = true;
+   m_compressCheck->SetValue(opts.isGz) ;
+   m_fileName->Clear();
+   m_fileName->AppendText(opts.filename);
+   //char cstring[1024];
+   //strcpy(cstring,opts.outdir);
+   //wxString outdir = wxString::FromUTF8(cstring);
+   wxString outdir = wxString::FromUTF8(opts.outdir);
+   if (outdir.length() < 1) 
+   	m_outputFolder->SetLabel("Input folder");
+   else { 
+   		if (outdir.Length() > 40) {
+   			outdir.Truncate(40);
+   			outdir.Append("...");
+   		}
+   		m_outputFolder->SetLabel(outdir);;
+   }
+   this->OnShowExampleFilename();
+   applicationBusy = false;
+}
+
+void MyFrame::OnFileName(wxCommandEvent& WXUNUSED(event))
+{
+	//only respond to inputs from the user, http://wxwidgets.10942.n7.nabble.com/wxTextCtrl-constructor-generates-EVT-TEXT-td24562.html
+	if (applicationBusy) return;
+	strncpy(opts.filename, (const char*)m_fileName->GetValue().mb_str(wxConvUTF8), 1023);
+	this->OnShowExampleFilename();
+}
+
+void MyFrame::OnCompressCheck(wxCommandEvent& WXUNUSED(event))
+{
+	opts.isGz = m_compressCheck->GetValue();
+	this->OnShowExampleFilename();
+}
+
+void MyFrame::OnParRec(wxCommandEvent& WXUNUSED(event))
+{
+   wxFileDialog* fileDialog = new wxFileDialog(
+		this, _("Choose a file to covert"), wxEmptyString, wxEmptyString, 
+		_("PAR/REC Image (*.par)||*.par;*.PAR"),
+		wxFD_OPEN, wxDefaultPosition); 
+   if (fileDialog->ShowModal() == wxID_OK) {
+		//wxString file = fileDialog->GetPath();
+   		//wxMessageBox(file);
+   		char fname[1024];
+    	strncpy(fname, (const char*)fileDialog->GetPath().mb_str(wxConvUTF8), 1023);
+    	OnProcessFile (fname);
+   }
+   fileDialog->Destroy();
+}
+
+void MyFrame::OnDicom(wxCommandEvent& WXUNUSED(event))
+{
+   wxString pth = wxT("");
+   wxDirDialog dirDialog(this, wxT("Select the DICOM folder"),pth, wxDD_NEW_DIR_BUTTON);
+   if (dirDialog.ShowModal() != wxID_OK) return;
+   //wxString dir = dirDialog.GetPath();
+   char fname[1024];
+   strncpy(fname, (const char*)dirDialog.GetPath().mb_str(wxConvUTF8), 1023);
+   OnProcessFile (fname);
+   //wxMessageBox(dir);
+}
+
+void MyFrame::OnCopy(wxCommandEvent& WXUNUSED(event))
+{
+	m_textctrl->SelectAll();
+	m_textctrl->Copy();
+	m_textctrl->SetSelection(m_textctrl->XYToPosition(0,0),m_textctrl->XYToPosition(0,0));
+}
+
+void MyFrame::OnSetOutputFolder(wxCommandEvent& WXUNUSED(event))
+{
+   wxString pth = wxT("");
+   wxDirDialog dialog(this, wxT("Select out folder (or cancel to use input folder)"),pth, wxDD_NEW_DIR_BUTTON);
+   if (dialog.ShowModal() != wxID_OK) return;
+   wxString dir = dialog.GetPath();
+   strncpy(opts.outdir, (const char*)dialog.GetPath().mb_str(wxConvUTF8), 1023);
+   this->OnShowPrefs();
+}
+
+void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+{
+    Close(true);
+}
+
+void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+{
+    wxMessageBox("Chris Rorden :: www.mricro.com","dcm2nii",  wxOK, this);
+}
+
+
diff --git a/wxWidgets/nifti1_io_core.c b/wxWidgets/nifti1_io_core.c
new file mode 100644
index 0000000..1469ef3
--- /dev/null
+++ b/wxWidgets/nifti1_io_core.c
@@ -0,0 +1,472 @@
+#include "nifti1_io_core.h"
+#include <math.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+#include <unistd.h>
+#include <stdio.h>
+
+
+void nifti_swap_8bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    register unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+7;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 8;
+    }
+    return ;
+}
+
+void nifti_swap_4bytes( size_t n , void *ar )    // 4 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ;
+    register unsigned char tval ;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp1 = cp0; cp2 = cp0+3;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1++;  cp2--;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp0 += 4;
+    }
+    return ;
+}
+
+void nifti_swap_2bytes( size_t n , void *ar )    // 2 bytes at a time
+{
+    register size_t ii ;
+    unsigned char * cp1 = (unsigned char *)ar, * cp2 ;
+    unsigned char   tval;
+    for( ii=0 ; ii < n ; ii++ ){
+        cp2 = cp1 + 1;
+        tval = *cp1;  *cp1 = *cp2;  *cp2 = tval;
+        cp1 += 2;
+    }
+    return ;
+}
+
+int isSameFloat (float a, float b) {
+    return (fabs (a - b) <= FLT_EPSILON);
+}
+
+vec3 setVec3(float x, float y, float z)
+{
+    vec3 v = {x, y, z};
+    return v;
+}
+
+vec4 setVec4(float x, float y, float z)
+{
+    vec4 v= {x, y, z, 1};
+    return v;
+}
+
+vec3 crossProduct(vec3 u, vec3 v)
+{
+    return setVec3(u.v[1]*v.v[2] - v.v[1]*u.v[2],
+                   -u.v[0]*v.v[2] + v.v[0]*u.v[2],
+                   u.v[0]*v.v[1] - v.v[0]*u.v[1]);
+}
+
+float dotProduct(vec3 u, vec3 v)
+{
+    return (u.v[0]*v.v[0] + v.v[1]*u.v[1] + v.v[2]*u.v[2]);
+}
+
+vec3 nifti_vect33_norm (vec3 v) { //normalize vector length
+    vec3 vO = v;
+    float vLen = sqrt( (v.v[0]*v.v[0])
+                      + (v.v[1]*v.v[1])
+                      + (v.v[2]*v.v[2]));
+    if (vLen <= FLT_EPSILON) return vO; //avoid divide by zero
+    for (int i = 0; i < 3; i++)
+        vO.v[i] = v.v[i]/vLen;
+    return vO;
+}
+
+vec3 nifti_vect33mat33_mul(vec3 v, mat33 m ) { //multiply vector * 3x3matrix
+    vec3 vO;
+    for (int i=0; i<3; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<3; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+vec4 nifti_vect44mat44_mul(vec4 v, mat44 m ) { //multiply vector * 4x4matrix
+    vec4 vO;
+    for (int i=0; i<4; i++) { //multiply Pcrs * m
+        vO.v[i] = 0;
+        for(int j=0; j<4; j++)
+            vO.v[i] += m.m[i][j]*v.v[j];
+    }
+    return vO;
+}
+
+mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4]) {
+    //create NIfTI header based on values from DICOM header
+    //note orient has 6 values, indexed from 1, patient position and xyzMM have 3 values indexed from 1
+    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] );
+    /* 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 ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[0][0] *= (float)val ; Q.m[0][1] *= (float)val ; Q.m[0][2] *= (float)val ;
+    } else {
+        Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ;
+    }
+    /* normalize row 2 */
+    val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ;
+    if( val > 0.0l ){
+        val = 1.0l / sqrt(val) ;
+        Q.m[1][0] *= (float)val ; Q.m[1][1] *= (float)val ; Q.m[1][2] *= (float)val ;
+    } else {
+        Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ;
+    }
+    /* row 3 is the cross product of rows 1 and 2*/
+    Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ;  /* cross */
+    Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ;  /* product */
+    Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ;
+    Q = nifti_mat33_transpose(Q);
+    if (nifti_mat33_determ(Q) < 0.0) {
+        Q.m[0][2] = -Q.m[0][2];
+        Q.m[1][2] = -Q.m[1][2];
+        Q.m[2][2] = -Q.m[2][2];
+    }
+    //next scale matrix
+    LOAD_MAT33(diagVox, xyzMM[1],0.0l,0.0l, 0.0l,xyzMM[2],0.0l, 0.0l,0.0l, xyzMM[3]);
+    Q = nifti_mat33_mul(Q,diagVox);
+    mat44 Q44; //4x4 matrix includes translations
+    LOAD_MAT44(Q44, Q.m[0][0],Q.m[0][1],Q.m[0][2],patientPosition[1],
+               Q.m[1][0],Q.m[1][1],Q.m[1][2],patientPosition[2],
+               Q.m[2][0],Q.m[2][1],Q.m[2][2],patientPosition[3]);
+    return Q44;
+}
+
+float nifti_mat33_determ( mat33 R )   /* determinant of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 ;
+    /*  INPUT MATRIX:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  /* [ r11 r12 r13 ] */
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  /* [ r21 r22 r23 ] */
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  /* [ r31 r32 r33 ] */
+    return (float)(r11*r22*r33-r11*r32*r23-r21*r12*r33
+                   +r21*r32*r13+r31*r12*r23-r31*r22*r13) ;
+}
+
+mat33 nifti_mat33_mul( mat33 A , mat33 B )  /* multiply 2 3x3 matrices */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 C ; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j] ;
+    return C ;
+}
+
+mat44 nifti_mat44_mul( mat44 A , mat44 B )  /* multiply 2 3x3 matrices */
+{
+    mat44 C ; int i,j ;
+    for( i=0 ; i < 4 ; i++ )
+        for( j=0 ; j < 4; j++ )
+            C.m[i][j] =  A.m[i][0] * B.m[0][j]
+            + A.m[i][1] * B.m[1][j]
+            + A.m[i][2] * B.m[2][j]
+            + A.m[i][3] * B.m[3][j];
+    return C ;
+}
+
+mat33 nifti_mat33_transpose( mat33 A )  /* transpose 3x3 matrix */
+//see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+{
+    mat33 B; int i,j ;
+    for( i=0 ; i < 3 ; i++ )
+        for( j=0 ; j < 3 ; j++ )
+            B.m[i][j] =  A.m[j][i];
+    return B;
+}
+
+mat33 nifti_mat33_inverse( mat33 R )   /* inverse of 3x3 matrix */
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ;
+    mat33 Q ;
+    //  INPUT MATRIX:
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    return Q ;
+}
+
+float nifti_mat33_rownorm( mat33 A )  // max row norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ;
+    r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ;
+    r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+float nifti_mat33_colnorm( mat33 A )  // max column norm of 3x3 matrix
+{
+    float r1,r2,r3 ;
+    r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ;
+    r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ;
+    r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ;
+    if( r1 < r2 ) r1 = r2 ;
+    if( r1 < r3 ) r1 = r3 ;
+    return r1 ;
+}
+
+mat33 nifti_mat33_polar( mat33 A )
+{
+    mat33 X , Y , Z ;
+    float alp,bet,gam,gmi , dif=1.0 ;
+    int k=0 ;
+    X = A ;
+    // force matrix to be nonsingular
+    gam = nifti_mat33_determ(X) ;
+    while( gam == 0.0 ){        // perturb matrix
+        gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ;
+        X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ;
+        gam = nifti_mat33_determ(X) ;
+    }
+    while(1){
+        Y = nifti_mat33_inverse(X) ;
+        if( dif > 0.3 ){     // far from convergence
+            alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ;
+            bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ;
+            gam = sqrt( bet / alp ) ;
+            gmi = 1.0 / gam ;
+        } else
+            gam = gmi = 1.0 ;  // close to convergence
+        Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ;
+        Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ;
+        Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ;
+        Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ;
+        Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ;
+        Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ;
+        Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ;
+        Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ;
+        Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ;
+        dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1])
+        +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0])
+        +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2])
+        +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1])
+        +fabs(Z.m[2][2]-X.m[2][2])                          ;
+        k = k+1 ;
+        if( k > 100 || dif < 3.e-6 ) break ;  // convergence or exhaustion
+        X = Z ;
+    }
+    return Z ;
+}
+
+void nifti_mat44_to_quatern( mat44 R ,
+                            float *qb, float *qc, float *qd,
+                            float *qx, float *qy, float *qz,
+                            float *dx, float *dy, float *dz, float *qfac )
+{
+    double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ;
+    double xd,yd,zd , a,b,c,d ;
+    mat33 P,Q ;
+    // offset outputs are read write out of input matrix
+    ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ;
+    // load 3x3 matrix into local variables */
+    r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ;
+    r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ;
+    r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ;
+    // compute lengths of each column; these determine grid spacings
+    xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ;
+    yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ;
+    zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ;
+    // if a column length is zero, patch the trouble
+    if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; }
+    if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; }
+    if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; }
+    // assign the output lengths */
+    ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ;
+    // normalize the columns */
+    r11 /= xd ; r21 /= xd ; r31 /= xd ;
+    r12 /= yd ; r22 /= yd ; r32 /= yd ;
+    r13 /= zd ; r23 /= zd ; r33 /= zd ;
+    /* At this point, the matrix has normal columns, but we have to allow
+     for the fact that the hideous user may not have given us a matrix
+     with orthogonal columns.
+     So, now find the orthogonal matrix closest to the current matrix.
+     One reason for using the polar decomposition to get this
+     orthogonal matrix, rather than just directly orthogonalizing
+     the columns, is so that inputting the inverse matrix to R
+     will result in the inverse orthogonal matrix at this point.
+     If we just orthogonalized the columns, this wouldn't necessarily hold. */
+    Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; // load Q
+    Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ;
+    Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ;
+    P = nifti_mat33_polar(Q) ;  // P is orthog matrix closest to Q
+    r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; // unload
+    r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ;
+    r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ;
+    //                            [ r11 r12 r13 ]
+    // at this point, the matrix  [ r21 r22 r23 ] is orthogonal
+    //                            [ r31 r32 r33 ]
+    // compute the determinant to determine if it is proper
+    zd = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;  // should be -1 or 1
+    if( zd > 0 ){             // proper
+        ASSIF(qfac,1.0) ;
+    } else {                  // improper ==> flip 3rd column
+        ASSIF(qfac,-1.0) ;
+        r13 = -r13 ; r23 = -r23 ; r33 = -r33 ;
+    }
+    // now, compute quaternion parameters
+    a = r11 + r22 + r33 + 1.0l ;
+    if( a > 0.5l ){                // simplest case
+        a = 0.5l * sqrt(a) ;
+        b = 0.25l * (r32-r23) / a ;
+        c = 0.25l * (r13-r31) / a ;
+        d = 0.25l * (r21-r12) / a ;
+    } else {                       // trickier case
+        xd = 1.0 + r11 - (r22+r33) ;  // 4*b*b
+        yd = 1.0 + r22 - (r11+r33) ;  // 4*c*c
+        zd = 1.0 + r33 - (r11+r22) ;  // 4*d*d
+        if( xd > 1.0 ){
+            b = 0.5l * sqrt(xd) ;
+            c = 0.25l* (r12+r21) / b ;
+            d = 0.25l* (r13+r31) / b ;
+            a = 0.25l* (r32-r23) / b ;
+        } else if( yd > 1.0 ){
+            c = 0.5l * sqrt(yd) ;
+            b = 0.25l* (r12+r21) / c ;
+            d = 0.25l* (r23+r32) / c ;
+            a = 0.25l* (r13-r31) / c ;
+        } else {
+            d = 0.5l * sqrt(zd) ;
+            b = 0.25l* (r13+r31) / d ;
+            c = 0.25l* (r23+r32) / d ;
+            a = 0.25l* (r21-r12) / d ;
+        }
+        //        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; }
+        if( a < 0.0l ){ b=-b ; c=-c ; d=-d; } //a discarded...
+    }
+    ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ;
+    return ;
+}
+
+mat44 nifti_quatern_to_mat44( float qb, float qc, float qd,
+                             float qx, float qy, float qz,
+                             float dx, float dy, float dz, float qfac )
+{
+    mat44 R ;
+    double a,b=qb,c=qc,d=qd , xd,yd,zd ;
+    
+    /* last row is always [ 0 0 0 1 ] */
+    
+    R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0f ; R.m[3][3]= 1.0f ;
+    
+    /* compute a parameter from b,c,d */
+    
+    a = 1.0l - (b*b + c*c + d*d) ;
+    if( a < 1.e-7l ){                   /* special case */
+        a = 1.0l / sqrt(b*b+c*c+d*d) ;
+        b *= a ; c *= a ; d *= a ;        /* normalize (b,c,d) vector */
+        a = 0.0l ;                        /* a = 0 ==> 180 degree rotation */
+    } else{
+        a = sqrt(a) ;                     /* angle = 2*arccos(a) */
+    }
+    
+    /* load rotation matrix, including scaling factors for voxel sizes */
+    
+    xd = (dx > 0.0) ? dx : 1.0l ;       /* make sure are positive */
+    yd = (dy > 0.0) ? dy : 1.0l ;
+    zd = (dz > 0.0) ? dz : 1.0l ;
+    
+    if( qfac < 0.0 ) zd = -zd ;         /* left handedness? */
+    
+    R.m[0][0] = (float)( (a*a+b*b-c*c-d*d) * xd) ;
+    R.m[0][1] = 2.0l * (b*c-a*d        ) * yd ;
+    R.m[0][2] = 2.0l * (b*d+a*c        ) * zd ;
+    R.m[1][0] = 2.0l * (b*c+a*d        ) * xd ;
+    R.m[1][1] = (float)( (a*a+c*c-b*b-d*d) * yd) ;
+    R.m[1][2] = 2.0l * (c*d-a*b        ) * zd ;
+    R.m[2][0] = 2.0l * (b*d-a*c        ) * xd ;
+    R.m[2][1] = 2.0l * (c*d+a*b        ) * yd ;
+    R.m[2][2] = (float)( (a*a+d*d-c*c-b*b) * zd) ;
+    
+    /* load offsets */
+    
+    R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ;
+    
+    return R ;
+}
+
+mat44 nifti_mat44_inverse( mat44 R )
+{
+    double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ;
+    mat44 Q ;
+    /*  INPUT MATRIX IS:  */
+    r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2];  // [ r11 r12 r13 v1 ]
+    r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2];  // [ r21 r22 r23 v2 ]
+    r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2];  // [ r31 r32 r33 v3 ]
+    v1  = R.m[0][3]; v2  = R.m[1][3]; v3  = R.m[2][3];  // [  0   0   0   1 ]
+    deti = r11*r22*r33-r11*r32*r23-r21*r12*r33
+    +r21*r32*r13+r31*r12*r23-r31*r22*r13 ;
+    if( deti != 0.0l ) deti = 1.0l / deti ;
+    Q.m[0][0] = deti*( r22*r33-r32*r23) ;
+    Q.m[0][1] = deti*(-r12*r33+r32*r13) ;
+    Q.m[0][2] = deti*( r12*r23-r22*r13) ;
+    Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3
+                      -r22*v1*r33-r32*r13*v2+r32*v1*r23) ;
+    Q.m[1][0] = deti*(-r21*r33+r31*r23) ;
+    Q.m[1][1] = deti*( r11*r33-r31*r13) ;
+    Q.m[1][2] = deti*(-r11*r23+r21*r13) ;
+    Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3
+                      +r21*v1*r33+r31*r13*v2-r31*v1*r23) ;
+    Q.m[2][0] = deti*( r21*r32-r31*r22) ;
+    Q.m[2][1] = deti*(-r11*r32+r31*r12) ;
+    Q.m[2][2] = deti*( r11*r22-r21*r12) ;
+    Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3
+                      -r21*r32*v1-r31*r12*v2+r31*r22*v1) ;
+    Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ;
+    Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0
+    return Q ;
+}
+
+
+
+
+
+
+
+
+
diff --git a/wxWidgets/nii_dicom.c b/wxWidgets/nii_dicom.c
new file mode 100644
index 0000000..9e5e381
--- /dev/null
+++ b/wxWidgets/nii_dicom.c
@@ -0,0 +1,1852 @@
+//#define MY_DEBUG
+
+#include "nifti1.h"
+#include "nii_dicom.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h> //toupper
+#include <math.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <float.h>
+#include <stdint.h>
+#include "nifti1_io_core.h"
+#ifdef myUseCOut
+#include <iostream>
+#endif
+
+#ifndef M_PI
+#define M_PI           3.14159265358979323846
+#endif
+
+#ifdef MY_DEBUG
+float deFuzz(float v) {
+    if (fabs(v) < 0.00001)
+        return 0;
+    else
+        return v;
+    
+}
+
+void reportMat33(char *str, mat33 A) {
+    printf("%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]));
+}
+
+void reportMat44(char *str, mat44 A) {
+    printf("%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]));
+}
+#endif
+
+
+int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, mat44 *R){
+    //returns slice direction: 1=sag,2=coronal,3=axial, -= flipped
+    if (h->dim[3] < 2) return 0; //don't care direction for single slice
+    int iSL = 1; //find Z-slice direction: row with highest magnitude of 3rd column
+    if ( (fabs(R->m[1][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[1][2]) >= fabs(R->m[2][2]))) iSL = 2; //
+    if ( (fabs(R->m[2][2]) >= fabs(R->m[0][2]))
+        && (fabs(R->m[2][2]) >= fabs(R->m[1][2]))) iSL = 3; //axial acquisition
+    float pos = NAN;
+    if ( !isnan(d2.patientPosition[iSL]) ) { //patient position fields exist
+        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);
+#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);
+#endif
+    }
+    if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) )
+        pos = d.stackOffcentre[iSL];
+    if (isnan(pos) && ( !isnan(d.lastScanLoc)) )
+        pos = d.lastScanLoc;
+    vec4 x;
+    x.v[0] = 0; x.v[1] = 0; x.v[2]=h->dim[3]-1; x.v[3] = 1;
+    vec4 pos1v = nifti_vect44mat44_mul(x, *R);
+    float pos1 = pos1v.v[iSL-1];//-1 as C indexed from 0
+    bool flip = false;
+    if (!isnan(pos)) // we have real SliceLocation for last slice or volume center
+        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
+#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
+    }
+    if (flip) {
+        for (int i = 0; i < 4; i++)
+            R->m[i][2] = -R->m[i][2];
+    }
+#ifdef MY_DEBUG
+    printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
+    reportMat44("Rout",*R);
+    printf("iSL = %d\n",iSL);
+    printf(" pos1 = %f\n",pos1);
+#endif
+    if (flip)
+        return -iSL;
+    else
+        return iSL;
+} //verify_slice_dir()
+
+void setQSForm(struct nifti_1_header *h, mat44 Q44) {
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->srow_x[0] = Q44.m[0][0];
+    h->srow_x[1] = Q44.m[0][1];
+    h->srow_x[2] = Q44.m[0][2];
+    h->srow_x[3] = Q44.m[0][3];
+    h->srow_y[0] = Q44.m[1][0];
+    h->srow_y[1] = Q44.m[1][1];
+    h->srow_y[2] = Q44.m[1][2];
+    h->srow_y[3] = Q44.m[1][3];
+    h->srow_z[0] = Q44.m[2][0];
+    h->srow_z[1] = Q44.m[2][1];
+    h->srow_z[2] = Q44.m[2][2];
+    h->srow_z[3] = Q44.m[2][3];
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( Q44 , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    h->qform_code = NIFTI_XFORM_SCANNER_ANAT;
+} //setQSForm()
+
+
+
+int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_1_header *h) { //fill header s and q form
+    //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
+    //returns sliceDirection: 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
+    h->sform_code = NIFTI_XFORM_UNKNOWN;
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    bool isOK = false;
+    for (int i = 1; i <= 6; i++)
+        if (d.orient[i] != 0.0) isOK = true;
+    if (!isOK) return sliceDir;
+    mat44 Q44 = nifti_dicom2mat(d.orient, d.patientPosition, d.xyzMM);
+    if (d.CSA.mosaicSlices > 1) {
+        double nRowCol = ceil(sqrt(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;
+        Q44.m[0][3] =(Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3];
+        Q44.m[1][3] =(Q44.m[1][0]*lFactorX)+(Q44.m[1][1]*lFactorY)+Q44.m[1][3];
+        Q44.m[2][3] =(Q44.m[2][0]*lFactorX)+(Q44.m[2][1]*lFactorY)+Q44.m[2][3];
+        
+        /* #ifdef obsolete_mosaic_flip
+         double val = d.xyzDim[2]/nRowCol; //obsolete!!!
+         //Q44 now equals 'dicom_to_patient' in spm_dicom_convert
+         mat44 patient_to_tal, analyze_to_dicom;
+         LOAD_MAT44(patient_to_tal, -1.0l,0.0l,0.0l,0.0l, 0.0l,-1.0l,0.0l,0.0l, 0.0l,0.0l,1.0l,0.0l);
+         LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,-1.0l, 0.0l,-1.0l,0.0l,val, 0.0l,0.0l,1.0l,-1.0l);
+         Q44 = nifti_mat44_mul(patient_to_tal,Q44);
+         Q44 = nifti_mat44_mul(Q44,analyze_to_dicom);
+         //Q44 now equals 'mat' in spm_dicom_convert
+         //subasgn.m in SPM5 translates by one voxel...
+         LOAD_MAT44(analyze_to_dicom, 1.0l,0.0l,0.0l,1.0l, 0.0l,1.0l,0.0l,1.0l, 0.0l,0.0l,1.0l,1.0l);
+         Q44 = nifti_mat44_mul(Q44,analyze_to_dicom);
+         #else */
+        for (int c=0; c<2; c++)
+            for (int r=0; r<4; r++)
+                Q44.m[c][r] = -Q44.m[c][r];
+        // #endif
+        mat33 Q;
+        LOAD_MAT33(Q, d.orient[1], d.orient[4],d.CSA.sliceNormV[1],
+                   d.orient[2],d.orient[5],d.CSA.sliceNormV[2],
+                   d.orient[3],d.orient[6],d.CSA.sliceNormV[3]);
+        if  (nifti_mat33_determ(Q) < 0) { //Siemens sagittal are R>>L, whereas NIfTI is L>>R, we retain Siemens order on disk so ascending is still ascending, but we need to have the spatial transform reflect this.
+            mat44 det;
+            sliceDir = kSliceOrientMosaicNegativeDeterminant; //we need to handle DTI vectors accordingly
+            LOAD_MAT44(det, 1.0l,0.0l,0.0l,0.0l, 0.0l,1.0l,0.0l,0.0l, 0.0l,0.0l,-1.0l,0.0l);
+            //patient_to_tal.m[2][3] = 1-d.CSA.MosaicSlices;
+            Q44 = nifti_mat44_mul(Q44,det);
+        }
+    } else { //not a mosaic
+        sliceDir = verify_slice_dir(d, d2, h, &Q44);
+        for (int c=0; c<4; c++)// LPS to nifti RAS, xform matrix before reorient
+            for (int r=0; r<2; r++) //swap rows 1 & 2
+                Q44.m[r][c] = - Q44.m[r][c];
+#ifdef MY_DEBUG
+        reportMat44("final",Q44);
+#endif
+    }
+    setQSForm(h,Q44);
+    return sliceDir;
+} //headerDcm2NiiSForm()
+
+int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h) { //final pass after de-mosaic
+    char txt[1024] = {""};
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d.CSA.sliceOrder;
+    if (h->slice_code == NIFTI_SLICE_UNKNOWN) h->slice_code = d2.CSA.sliceOrder; //sometimes the first slice order is screwed up https://github.com/eauerbach/CMRR-MB/issues/29
+    sprintf(txt, "TE=%.2g;Time=%.3f", d.TE,d.acquisitionTime);// d.dateTime);
+    if (d.CSA.phaseEncodingDirectionPositive > 0) {
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";phase+=%d", d.CSA.phaseEncodingDirectionPositive);
+        strcat(txt,dtxt);
+    }
+    if ((d.CSA.bandwidthPerPixelPhaseEncode > 0) && ((d.phaseEncodingRC =='C') || (d.phaseEncodingRC =='R'))) {
+        float dwellTime = 0;
+        if (d.phaseEncodingRC =='C')
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+        else
+            dwellTime =  1000/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
+        char dtxt[1024] = {""};
+        sprintf(dtxt, ";dwell=%.3f", dwellTime);
+        strcat(txt,dtxt);
+    }
+    //printf(" Description= '%s'  [length=%lu]\n",txt, strlen(txt));
+    //x strlcpy(h->descrip,txt,80);
+    snprintf(h->descrip,80, "%s",txt);
+    if (strlen(d.imageComments) > 0)
+        snprintf(h->aux_file,24,"%s",d.imageComments);
+    return headerDcm2NiiSForm(d,d2, h);
+} //headerDcm2Nii2()
+
+int dcmStrLen (int len) {
+    if (len < kDICOMStr)
+        return len+1;
+    else
+        return kDICOMStr;
+} //dcmStrLen()
+
+struct TDICOMdata clear_dicom_data() {
+    struct TDICOMdata d;
+    
+    d.locationsInAcquisition = 0;
+    for (int i=0; i < 4; i++) {
+        for (int n=0; n < kMaxDTIv; n++)
+            d.CSA.dtiV[n][i] = 0;
+        d.patientPosition[i] = NAN;
+        //d.patientPosition2nd[i] = NAN; //used to distinguish XYZT vs XYTZ for Philips 4D
+        d.patientPositionLast[i] = NAN; //used to compute slice direction for Philips 4D
+        d.stackOffcentre[i] = NAN;
+        d.angulation[i] = 0.0f;
+        d.xyzMM[i] = 1;
+    }
+    d.CSA.numDti = 0;
+    for (int i=0; i < 5; i++) d.xyzDim[i] = 1;
+        for (int i = 0; i < 7; i++) d.orient[i] = 0.0f;
+            d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false;
+            d.isHasPhase = false;
+            d.isHasMagnitude = false;
+            d.sliceOrient = kSliceOrientUnknown;
+            
+            //strcpy(d.sliceOrient,"\n");
+            strcpy(d.patientName, "John_Doe");
+            strcpy(d.patientID, "ID123");
+            strcpy(d.imageComments, "imgComments");
+            strcpy(d.studyDate, "1/1/1977");
+            strcpy(d.studyTime, "11:11:11");
+            d.dateTime = (double)19770703150928.0;
+            d.acquisitionTime = 0.0f;
+            strcpy(d.protocolName, "MPRAGE");
+            d.manufacturer = kMANUFACTURER_UNKNOWN;
+            d.isPlanarRGB = false;
+            d.lastScanLoc = NAN;
+            d.TR = 0;
+            d.TE = 0;
+            //d.locationsInAcquisition = 0;
+            d.numberOfDynamicScans = 0;
+            d.imageNum = 0;
+            d.intenScale = 1;
+            d.intenIntercept = 0;
+            d.seriesNum = 1;
+            d.acquNum = 0;
+            d.imageNum = 1;
+            d.imageStart = 0;
+            d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE
+            d.bitsAllocated = 16;//bits
+            d.bitsStored = 0;
+            d.samplesPerPixel = 1;
+            d.isValid = false;
+            d.isSigned = false; //default is unsigned!
+            d.isExplicitVR = true;
+            d.isLittleEndian = true; //DICOM initially always little endian
+            d.converted2NII = 0;
+            d.phaseEncodingRC = '?';
+            d.CSA.bandwidthPerPixelPhaseEncode = 0.0;
+            d.CSA.mosaicSlices = 0;
+            d.CSA.sliceOrder = NIFTI_SLICE_UNKNOWN;
+            d.CSA.protocolSliceNumber1 = 0;
+            d.CSA.phaseEncodingDirectionPositive = -1; //unknown
+            return d;
+} //clear_dicom_data()
+
+void dcmStrDigitsOnly(char* lStr) {
+    //e.g. change "H11" to " 11"
+    size_t len = strlen(lStr);
+    if (len < 1) return;
+    for (int i = 0; i < len; i++)
+        if (!isdigit(lStr[i]) )
+            lStr[i] = ' ';
+    
+}
+
+
+void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
+    //char test[] = " 1     2    3    ";
+    //lLength = (int)strlen(test);
+    if (lLength < 1) return;
+    char cString[lLength+1];
+    cString[lLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lLength);
+    //memcpy(cString, test, lLength);
+    for (int i = 0; i < lLength; i++)
+        if (cString[i]==' ') cString[i] = '_';
+    int len = 1;
+    for (int i = 1; i < lLength; i++) { //remove repeated "_"
+        if ((cString[i-1]!='_') || (cString[i]!='_')) {
+            cString[len] =cString[i];
+            len++;
+        }
+    } //for each item
+    if (cString[len-1] == '_') len--;
+    //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
+    cString[len] = 0; //null-terminate, strlcpy does this anyway
+    len = dcmStrLen(len);
+    //strlcpy(lOut,cString,len);
+    memcpy(lOut,cString,len-1);
+    lOut[len-1] = 0;
+    //printf(">>%s<<>>%s<<\n",cString,lOut);
+    //lOut[len-1]='z';
+    //printf(">>%s<<>>%s<<\n",cString,lOut);
+    //strlcpy(lOut,cString,len);
+} //dcmStr()
+
+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;
+    memcpy(&retVal, (char*)&lBuffer[0], 4);
+    if (!swap) return retVal;
+    char *floatToConvert = ( char* ) & lBuffer;
+    char *returnFloat = ( char* ) & retVal;
+    //swap the bytes into a temporary buffer
+    returnFloat[0] = floatToConvert[3];
+    returnFloat[1] = floatToConvert[2];
+    returnFloat[2] = floatToConvert[1];
+    returnFloat[3] = floatToConvert[0];
+    //printf("swapped val = %f\n",retVal);
+    return retVal;
+} //dcmFloat()
+
+int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //read binary 16 or 32 bit integer
+    if (littleEndian) {
+        if (lByteLength <= 3)
+            return  lBuffer[0] | (lBuffer[1]<<8); //shortint vs word?
+        return lBuffer[0]+(lBuffer[1]<<8)+(lBuffer[2]<<16)+(lBuffer[3]<<24); //shortint vs word?
+    }
+    if (lByteLength <= 3)
+        return  lBuffer[1] | (lBuffer[0]<<8); //shortint vs word?
+    return lBuffer[3]+(lBuffer[2]<<8)+(lBuffer[1]<<16)+(lBuffer[0]<<24); //shortint vs word?
+} //dcmInt()
+
+int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
+    char cString[lByteLength+1];
+    cString[lByteLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
+    return atoi(cString);
+} //dcmStrInt()
+
+int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
+    if (lByteLength < 2) return kMANUFACTURER_UNKNOWN;
+    char cString[lByteLength+1];
+    cString[lByteLength] =0;
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    //printf("MANU %s\n",cString);
+    if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I'))
+        return kMANUFACTURER_SIEMENS;
+    if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E'))
+        return kMANUFACTURER_GE;
+    if ((toupper(cString[0])== 'P') && (toupper(cString[1])== 'H'))
+        return kMANUFACTURER_PHILIPS;
+    return kMANUFACTURER_UNKNOWN;
+} //dcmStrManufacturer
+
+typedef struct __attribute__((packed)) {
+    char name[64]; //null-terminated
+    int32_t vm;
+    char vr[4]; //  possibly nul-term string
+    int32_t syngodt;//  ??
+    int32_t nitems;// number of items in CSA
+    int32_t xx;// maybe == 77 or 205
+} TCSAtag; //Siemens csa tag structure
+typedef struct __attribute__((packed)) {
+    int32_t xx1, xx2_Len, xx3_77, xx4;
+} TCSAitem; //Siemens csa item structure
+
+float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *ItemsOK) {
+    //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
+    //if lnItems == 1, returns first item, if lnItems > 1 returns index of final successful conversion
+    TCSAitem itemCSA;
+    *ItemsOK = 0;
+    if (nItems < 1)  return 0.0f;
+    int lPos = 0;
+    for (int lI = 1; lI <= nItems; lI++) {
+        memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+        lPos +=sizeof(itemCSA);
+        if (itemCSA.xx2_Len > 0) {
+            char cString[itemCSA.xx2_Len];
+            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);
+            Floats[lI] = atof(cString);
+            *ItemsOK = lI; //some sequences have store empty items
+        }
+    } //for each item
+    return Floats[1];
+} //csaMultiFloat()
+
+int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, bool isVerbose) {
+    //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]);
+    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
+    int lnTag = buff[lPos]+(buff[lPos+1]<<8)+(buff[lPos+2]<<16)+(buff[lPos+3]<<24);
+    if (buff[lPos+4] != 77) return EXIT_FAILURE;
+    lPos += 8; //skip 8 bytes of data, 32-bit lnTag plus 77 00 00 0
+    TCSAtag tagCSA;
+    TCSAitem itemCSA;
+    int itemsOK;
+    float lFloats[7];
+    for (int lT = 1; lT <= lnTag; lT++) {
+        memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag
+        lPos +=sizeof(tagCSA);
+        //printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems);
+        if (tagCSA.nitems > 0) {
+            if (strcmp(tagCSA.name, "NumberOfImagesInMosaic") == 0)
+                CSA->mosaicSlices = round(csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            else if (strcmp(tagCSA.name, "B_value") == 0) {
+                CSA->dtiV[0][0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK);
+                CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag
+            }
+            else if ((strcmp(tagCSA.name, "DiffusionGradientDirection") == 0) && (tagCSA.nitems > 2)){
+                CSA->dtiV[0][1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
+                CSA->dtiV[0][2] = lFloats[2];
+                CSA->dtiV[0][3] = lFloats[3];
+                if (isVerbose)
+                    printf("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]);
+            } 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)  ){
+                float sliceTimes[tagCSA.nitems+1];
+                csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK);
+                float minTimeIndex, minTimeValue, timeValue1;
+                CSA->multiBandFactor = 1;
+                timeValue1 = sliceTimes[1];
+                minTimeIndex = 1;
+                minTimeValue= sliceTimes[1];
+                for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time
+                    if (sliceTimes[z] < minTimeValue) {
+                        minTimeValue = sliceTimes[z];
+                        minTimeIndex = z;
+                    }
+                    if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++;
+                }
+                if (minTimeIndex == 2)
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC2;// e.g. 3,1,4,2
+                else if (minTimeIndex == (itemsOK-1))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC2;// e.g. 4,3,2,1
+                else if ((minTimeIndex == 1) && (sliceTimes[2] < sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_INC;
+                else if ((minTimeIndex == 1) && (sliceTimes[2] > sliceTimes[3]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_INC;
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] < sliceTimes[itemsOK]))
+                    CSA->sliceOrder = NIFTI_SLICE_SEQ_DEC;
+                else if ((minTimeIndex == itemsOK) && (sliceTimes[itemsOK-1] > sliceTimes[itemsOK-2]))
+                    CSA->sliceOrder = NIFTI_SLICE_ALT_DEC;
+                else {
+                    /*NSMutableArray *sliceTimesNS = [NSMutableArray arrayWithCapacity:tagCSA.nitems];
+                     for (int z = 1; z <= itemsOK; z++)
+                     [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");
+                }
+            } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0)
+                CSA->protocolSliceNumber1 = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0)
+                CSA->phaseEncodingDirectionPositive = round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+            /*if (strcmp(tagCSA.name, "SlicePosition_PCS") == 0) {
+             float f =  (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
+             for (int k = 1; k <= tagCSA.nitems; k++)
+             printf("PCS %d = %f\n",k, lFloats[k]);
+             }
+             else {
+             printf("unused CSA tag %s with %d items\n",tagCSA.name, tagCSA.nitems);
+             }*/
+            for (int lI = 1; lI <= tagCSA.nitems; lI++) {
+                memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
+                lPos +=sizeof(itemCSA);
+                lPos += ((itemCSA.xx2_Len +3)/4)*4;
+            }
+        } //if at least 1 item
+    }// for lT 1..lnTag
+    return EXIT_SUCCESS;
+} // readCSAImageHeader()
+
+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;
+    char cString[lByteLength+1];
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    cString[lByteLength] = 0; //null terminate
+    char *temp=( char *)malloc(lByteLength+1);
+    int f = 0,lStart = 0;
+    bool isOK = false;
+    for (int i = 0; i <= lByteLength; i++) {
+        if ((lBuffer[i] >= '0') && (lBuffer[i] <= '9')) isOK = true;
+        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);
+            if (f < lnFloats) {
+                f ++;
+                lFloats[f] = atof(temp);
+                //printf("%d == %f\n", f, atof(temp));
+            } //if f <= nFloats
+            lStart = i+1;
+        } //if isOK
+    }  //for i to length
+    free(temp);
+} //dcmMultiFloat()
+
+float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
+    char cString[lByteLength+1];
+    memcpy(cString, (char*)&lBuffer[0], lByteLength);
+    cString[lByteLength] = 0; //null terminate
+    return atof(cString);
+} //dcmStrFloat()
+
+/*
+ void dcmStrReport (int lByteLength, char lBuffer[]) {//print value at location
+ char cString[lByteLength+1];
+ memcpy(cString, (char*)&lBuffer[0], lByteLength);
+ cString[lByteLength] = 0; //null terminate
+ printf("%d dcmStrReport '%s'\n",lByteLength, cString);
+ } //dcmStrReport
+ */
+
+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);
+    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;
+    for (int i = 0; i < 10; i++) h->data_type[i] = 0;
+    for (int i = 0; i < 16; i++) h->intent_name[i] = 0;
+    if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3))
+        h->datatype = DT_RGB24;
+    else if ((d.bitsAllocated == 8) && (d.samplesPerPixel == 1))
+        h->datatype = DT_UINT8;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (d.isSigned))
+        h->datatype = DT_INT16;
+    else if ((d.bitsAllocated == 16) && (d.samplesPerPixel == 1) && (!d.isSigned))
+        h->datatype = DT_UINT16;
+    else if (d.bitsAllocated == 32)
+        h->datatype = DT_INT32;
+    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
+        
+        return EXIT_FAILURE;
+    }
+    if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16))
+        h->datatype = DT_INT16; // DT_INT16 is more widely supported, same represenation for values 0..32767
+    for (int i = 0; i < 8; i++) {
+        h->pixdim[i] = 0.0f;
+        h->dim[i] = 0;
+    }
+    h->regular = 114;
+    h->scl_inter = d.intenIntercept;
+    h->scl_slope = d.intenScale;
+    h->cal_max = 0;
+    h->cal_min = 0;
+    h->magic[0]='n';
+    h->magic[1]='+';
+    h->magic[2]='1';
+    h->magic[3]='\0';
+    h->vox_offset = d.imageStart;
+    h->bitpix = d.bitsAllocated * d.samplesPerPixel;
+    h->pixdim[1] = d.xyzMM[1];
+    h->pixdim[2] = d.xyzMM[2];
+    h->pixdim[3] = d.xyzMM[3];
+    h->pixdim[4] = d.TR/1000; //TR reported in msec, time is in sec
+    h->dim[1] = d.xyzDim[1];
+    h->dim[2] = d.xyzDim[2];
+    h->dim[3] = d.xyzDim[3];
+    h->dim[4] = d.xyzDim[4];
+    if (h->dim[4] < 2)
+        h->dim[0] = 3;
+    else
+        h->dim[0] = 4;
+    
+    for (int i = 0; i <= 3; i++) {
+        h->srow_x[i] = 0.0f;
+        h->srow_y[i] = 0.0f;
+        h->srow_z[i] = 0.0f;
+    }
+    h->srow_x[0] = -1;
+    h->srow_y[2] = 1;
+    h->srow_z[1] = -1;
+    h->srow_x[3] =  (h->dim[1] /2);
+    h->srow_y[3] = -(h->dim[3] /2);
+    h->srow_z[3] =  (h->dim[2] /2);
+    h->qform_code = NIFTI_XFORM_UNKNOWN;
+    h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
+    h->toffset = 0;
+    h->intent_code = NIFTI_INTENT_NONE;
+    h->dim_info = 0; //Freq, Phase and Slice all unknown
+    h->xyzt_units = NIFTI_UNITS_UNKNOWN;
+    h->slice_duration = 0; //avoid +inf/-inf, NaN
+    h->intent_p1 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p2 = 0;  //avoid +inf/-inf, NaN
+    h->intent_p3 = 0;  //avoid +inf/-inf, NaN
+    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);
+    return EXIT_SUCCESS;
+} // headerDcm2Nii()
+
+bool isFloatDiff (float a, float b) {
+    return (fabs (a - b) > FLT_EPSILON);
+} //isFloatDiff()
+
+ivec3 setVec3i(int x, int y, int z)
+{
+    ivec3 v = {x, y, z};
+    return v;
+} //setVec3i()
+
+mat33 nifti_mat33_reorder_cols( mat33 m, ivec3 v ) {
+    // matlab equivalent ret = m(:, v); where v is 1,2,3 [INDEXED FROM ONE!!!!]
+    mat33 ret;
+    for (int r=0; r<3; r++) {
+        for(int c=0; c<3; c++)
+            ret.m[r][c] = m.m[r][v.v[c]-1];
+    }
+    return ret;
+} //nifti_mat33_reorder_cols()
+
+/*bool isExt (char *file_name, const char* ext) {
+ char *p_extension;
+ if((p_extension = strrchr(file_name,'.')) != NULL )
+ if(strcmp(p_extension,ext) == 0) return true;
+ return false;
+ 
+ }*/
+
+void changeExt (char *file_name, const char* ext) {
+    char *p_extension;
+    p_extension = strrchr(file_name, '.');
+    if (p_extension)
+    {
+        strcpy(++p_extension, ext);
+    }
+} //changeExt()
+
+struct TDICOMdata  nii_readParRec (char * parname) {
+    struct TDICOMdata d = clear_dicom_data();
+    FILE *fp = fopen(parname, "r");
+    if (fp == NULL) return d;
+#define LINESZ 2048
+#define	kSlice	0
+#define	kEcho	1
+#define	kDyn	2
+#define	kCardiac	3
+#define	kImageType	4
+#define	kSequence	5
+#define	kIndex	6
+#define	kBitsPerVoxel	7
+#define	kXdim	9
+#define	kYdim	10
+#define	kRI	11
+#define	kRS	12
+#define	kSS	13
+#define	kAngulationAPs	16 //In V4, offcentre and Angulation labeled as y z x, but actually x y z!
+#define	kAngulationFHs	17
+#define	kAngulationRLs	18
+#define	kPositionAP	19
+#define	kPositionFH	20
+#define	kPositionRL	21
+#define	kThickmm	22
+#define	kGapmm	23
+#define kSliceOrients 25
+#define	kXmm	28
+#define	kYmm	29
+#define	kTEcho	30
+#define	kDynTime	31
+#define	kGradientNumber 42
+#define	kbval 33
+#define	kv1	47
+#define	kv2	45
+#define	kv3	46
+#define	kASL	48
+    char buff[LINESZ];
+    bool ADCwarning = false;
+    for (int n=0; kMaxDTIv < 4; n++)
+        d.CSA.dtiV[n][0] = -1; //set to impossible value to detect re-usage by ADC map
+    int parVers = 0;
+    int nCols = 26;
+    int slice = 0;
+    //int prevSliceIndex = 0; //index of prior slice: detect if images are not in order
+    const int kMaxCols = 49;
+    float *cols = (float *)malloc(sizeof(float) * kMaxCols);
+    char *p = fgets (buff, LINESZ, fp);
+    bool isIntenScaleVaries = false;
+    bool isIndexSequential = true;
+    while (p) {
+        if (strlen(buff) < 1)
+            continue;
+        if (buff[0] == '#') { //comment
+            char Comment[7][50];
+            sscanf(buff, "# %s %s\n", Comment[0], Comment[1]);
+            if (strcmp(Comment[1], "TRYOUT") == 0) {
+                sscanf(buff, "# %s %s %s %s %s %s V%s\n", Comment[0], Comment[1], Comment[2], Comment[3]
+                       ,Comment[4], Comment[5],Comment[6]);
+                parVers = (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
+                    return d;
+                    //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns
+                } else if (parVers < 41)
+                    nCols = 32; //e.g PAR 4.0
+                else if (parVers < 42)
+                    nCols = 47; //e.g. PAR 4.1
+                else
+                    nCols = kMaxCols; //e.g. PAR 4.2
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '#' comment
+        if (buff[0] == '.') { //tag
+            char Comment[8][50];
+            sscanf(buff, ". %s %s %s %s %s %s %s %s\n", Comment[0], Comment[1],Comment[2], Comment[3], Comment[4], Comment[5], Comment[6], Comment[7]);
+            
+            if ((strcmp(Comment[0], "Acquisition") == 0) && (strcmp(Comment[1], "nr") == 0)) {
+                d.acquNum = atoi( Comment[3]);
+                d.seriesNum = d.acquNum;
+            }
+            if ((strcmp(Comment[0], "Repetition") == 0) && (strcmp(Comment[1], "time") == 0))
+                d.TR = atof(Comment[4]);
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.patientName, Comment[3]);
+                strcat(d.patientName, Comment[4]);
+                strcat(d.patientName, Comment[5]);
+                strcat(d.patientName, Comment[6]);
+                strcat(d.patientName, Comment[7]);
+                //printf("%s\n",d.patientName);
+                
+            }
+            if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
+                strcpy(d.protocolName, Comment[3]);
+                strcat(d.protocolName, Comment[4]);
+                strcat(d.protocolName, Comment[5]);
+                strcat(d.protocolName, Comment[6]);
+                strcat(d.protocolName, Comment[7]);
+                //printf("%s\n",d.protocolName);
+            }
+            if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
+                strcpy(d.studyDate, Comment[3]);
+                strcpy(d.studyTime, Comment[5]);
+                //to do convert to traditional DICOM style date time
+            }
+            if ((strcmp(Comment[0], "Off") == 0) && (strcmp(Comment[1], "Centre") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.stackOffcentre[2] = atof(Comment[5]);
+                d.stackOffcentre[3] = atof(Comment[6]);
+                d.stackOffcentre[1] = atof(Comment[7]);
+            }
+            if ((strcmp(Comment[0], "Patient") == 0) && (strcmp(Comment[1], "position") == 0)) {
+                //Off Centre midslice(ap,fh,rl) [mm]
+                d.patientOrient[0] = toupper(Comment[3][0]);
+                d.patientOrient[1] = toupper(Comment[4][0]);
+                d.patientOrient[2] = toupper(Comment[5][0]);
+                d.patientOrient[3] = 0;
+            }
+            if ((strcmp(Comment[0], "Max.") == 0) && (strcmp(Comment[3], "slices/locations") == 0)) {
+                d.xyzDim[3] = atoi(Comment[5]);
+            }
+            p = fgets (buff, LINESZ, fp);//get next line
+            continue;
+        } //process '.' tag
+        if (strlen(buff) < 24) { //empty line
+            p = fgets (buff, LINESZ, fp);//get next line
+            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
+            return d;
+        }
+        for (int i = 0; i < nCols; i++)
+            cols[i] = strtof(p, &p); // p+1 skip comma, read a float
+        if ((cols[kIndex]) != slice) isIndexSequential = false; //slices 0,1,2.. should have indices 0,1,2,3...
+        slice ++;
+        if (slice == 1) {
+            //for (int i = 0; i < nCols; i++)
+            //    cols1[i] = cols[i]; //store first slice to see if dimensions or intensity scale varies between slices
+            d.xyzDim[1] = cols[kXdim];
+            d.xyzDim[2] = cols[kYdim];
+            d.xyzMM[1] = cols[kXmm];
+            d.xyzMM[2] = cols[kYmm];
+            d.xyzMM[3] = cols[kThickmm] + cols[kGapmm];
+            d.patientPosition[1] = cols[kPositionRL];
+            d.patientPosition[2] = cols[kPositionAP];
+            d.patientPosition[3] = cols[kPositionFH];
+            d.angulation[1] = cols[kAngulationRLs];
+            d.angulation[2] = cols[kAngulationAPs];
+            d.angulation[3] = cols[kAngulationFHs];
+            d.sliceOrient = cols[kSliceOrients];
+            d.TE = cols[kTEcho];
+            d.bitsAllocated = cols[kBitsPerVoxel];
+            d.bitsStored = cols[kBitsPerVoxel];
+            d.intenIntercept = cols[kRI];
+            d.intenScale = cols[kRS];
+        } 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
+                return d;
+            }
+            if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) ||
+                                                              (!isSameFloat(d.patientPosition[2],cols[kPositionAP])) ||
+                                                              (!isSameFloat(d.patientPosition[3],cols[kPositionFH])) ) )//this is the first slice with different position
+                d.patientPositionSequentialRepeats = slice-1;
+            
+            if ((d.intenScale != cols[kRS]) || (d.intenIntercept != cols[kRI]))
+                isIntenScaleVaries = true;
+        }
+        if (cols[kImageType] == 0) d.isHasMagnitude = true;
+        if (cols[kImageType] != 0) d.isHasPhase = true;
+        if (cols[kGradientNumber] > 0) {
+            int dir = cols[kGradientNumber];
+            if ((cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) {
+                if (d.CSA.dtiV[dir-1][0] >= 0) dir = dir + 1; //Philips often stores an ADC map along with B0 and weighted images, unfortunately they give it the same kGradientNumber as the B0! (seen in PAR V4.2)
+                //the logic here is that IF the gradient was previously used we increment the gradient number. This should provide compatibility when Philips fixes this bug
+                //it seems like the ADC is always saved as the final volume, so this solution SHOULD be foolproof.
+                ADCwarning = true;
+            }
+            if (dir > d.CSA.numDti) d.CSA.numDti = dir;
+
+            if (cols[dir] <= kMaxDTIv) {
+                d.CSA.dtiV[dir-1][0] = cols[kbval];
+                d.CSA.dtiV[dir-1][1] = cols[kv1];
+                d.CSA.dtiV[dir-1][2] = cols[kv2];
+                d.CSA.dtiV[dir-1][3] = cols[kv3];
+
+            } //save DTI direction
+        } //if DTI directions
+        //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
+        p = fgets (buff, LINESZ, fp);//get next line
+    }
+    free (cols);
+    fclose (fp);
+    d.isValid = true;
+    d.isSigned = 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 << "DTI directions"<<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");
+    
+    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 DTI directions\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 = M_PI/180;
+            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);
+    mat33 R = nifti_mat33_mul( rx,ry );
+    R = nifti_mat33_mul( R,rz);
+    ivec3 ixyz = setVec3i(1,2,3);
+    if (d.sliceOrient == kSliceOrientSag) {
+        ixyz = setVec3i(2,3,1);
+        for (int r = 0; r < 3; r++)
+            for (int c = 0; c < 3; c++)
+                if (c != 1) R.m[r][c] = -R.m[r][c]; //invert first and final columns
+    }else if (d.sliceOrient == kSliceOrientCor) {
+        ixyz = setVec3i(1,3,2);
+        for (int r = 0; r < 3; r++)
+            R.m[r][2] = -R.m[r][2]; //invert rows of final column
+    }
+    R = nifti_mat33_reorder_cols(R,ixyz); //dicom rotation matrix
+    d.orient[1] = R.m[0][0]; d.orient[2] = R.m[1][0]; d.orient[3] = R.m[2][0];
+    d.orient[4] = R.m[0][1]; d.orient[5] = R.m[1][1]; d.orient[6] = R.m[2][1];
+    mat33 diag;
+    LOAD_MAT33(diag, d.xyzMM[1],0.0f,0.0f,  0.0f,d.xyzMM[2],0.0f,  0.0f,0.0f, d.xyzMM[3]);
+    R= nifti_mat33_mul( R, diag );
+    mat44 R44;
+    LOAD_MAT44(R44, R.m[0][0],R.m[0][1],R.m[0][2],d.stackOffcentre[1],
+               R.m[1][0],R.m[1][1],R.m[1][2],d.stackOffcentre[2],
+               R.m[2][0],R.m[2][1],R.m[2][2],d.stackOffcentre[3]);
+    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;
+    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]);
+    eye= nifti_mat44_inverse( eye ); //we wish to compute R/eye, so compute invEye and calculate R*invEye
+    R44= nifti_mat44_mul( R44 , eye );
+    vec4 y;
+    y.v[0]=0.0f; y.v[1]=0.0f; y.v[2]=d.xyzDim[3]-1; y.v[3]=1.0f;
+    y= nifti_vect44mat44_mul(y, R44 );
+    int iOri = 2; //for axial, slices are 3rd dimenson (indexed from 0) (k)
+    if (d.sliceOrient == kSliceOrientSag) iOri = 0; //for sagittal, slices are 1st dimension (i)
+        if (d.sliceOrient == kSliceOrientCor) iOri = 1; //for coronal, slices are 2nd dimension (j)
+            if  (( (y.v[iOri]-R44.m[iOri][3])>0 ) == ( (y.v[iOri]-d.stackOffcentre[iOri+1])>0 ) ) {
+                d.patientPosition[1] = R44.m[0][3];
+                d.patientPosition[2] = R44.m[1][3];
+                d.patientPosition[3] = R44.m[2][3];
+                d.patientPositionLast[1] = y.v[0];
+                d.patientPositionLast[2] = y.v[1];
+                d.patientPositionLast[3] = y.v[2];
+            }else {
+                //d.patientPosition
+                d.patientPosition[1] = y.v[0];
+                d.patientPosition[2] = y.v[1];
+                d.patientPosition[3] = y.v[2];
+                d.patientPositionLast[1] = R44.m[0][3];
+                d.patientPositionLast[2] = R44.m[1][3];
+                d.patientPositionLast[3] = R44.m[2][3];
+            }
+    //finish up
+    changeExt (parname, "REC");
+    d.locationsInAcquisition = d.xyzDim[3];
+    d.manufacturer = kMANUFACTURER_PHILIPS;
+    d.imageStart = 0;
+    return d;
+} //nii_readParRec()
+
+size_t nii_ImgBytes(struct nifti_1_header hdr) {
+    //unsigned long imgsz = nii_ImgBytes(hdr);
+    size_t imgsz = hdr.bitpix/8;
+    for (int i = 1; i < 8; i++)
+        if (hdr.dim[i]  > 1)
+            imgsz = imgsz * hdr.dim[i];
+    return imgsz;
+} //nii_ImgBytes()
+
+unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, int nMosaicSlices, int ProtocolSliceNumber1) {
+    //demosaic http://nipy.org/nibabel/dicom/dicom_mosaic.html
+    if (nMosaicSlices < 2) return inImg;
+    //Byte inImg[ [img length] ];
+    //[img getBytes:&inImg length:[img length]];
+    int nRowCol = ceil(sqrt(nMosaicSlices));
+    int colBytes = hdr->dim[1]/nRowCol * hdr->bitpix/8;
+    int lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    int rowBytes = hdr->dim[1] * hdr->dim[2]/nRowCol * hdr->bitpix/8;
+    int col = 0;
+    int row = 0;
+    int lOutPos = 0;
+    hdr->dim[1] = hdr->dim[1]/nRowCol;
+    hdr->dim[2] = hdr->dim[2]/nRowCol;
+    hdr->dim[3] = nMosaicSlices;
+    size_t imgsz = nii_ImgBytes(*hdr);
+    unsigned char *outImg = (unsigned char *)malloc(imgsz);
+    for (int m=1; m <= nMosaicSlices; m++) {
+        int lPos = (row * rowBytes) + (col * colBytes);
+        for (int y = 0; y < hdr->dim[2]; y++) {
+            memcpy(&outImg[lOutPos], &inImg[lPos], colBytes); // dest, src, bytes
+            lPos += lineBytes;
+            lOutPos +=colBytes;
+        }
+        col ++;
+        if (col >= nRowCol) {
+            row ++;
+            col = 0;
+        } //start new column
+    } //for m = each mosaic slice
+    if (ProtocolSliceNumber1 > 1) {
+#ifdef myUseCOut
+     	std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl;
+#else
+        printf("WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED\n");
+#endif
+    }
+    /*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;
+     memcpy(&inImg[0], &outImg[0],sliceBytes*hdr->dim[3]); //copy data with reversed order dest, src, bytes
+     int lOutPos = sliceBytes * (hdr->dim[3]-1);
+     int lPos = 0;
+     for (int m=0; m < nMosaicSlices; m++) {
+     memcpy( &outImg[lOutPos], &inImg[lPos], sliceBytes);
+     lPos += sliceBytes;
+     lOutPos -= sliceBytes;
+     }
+     }*/
+    free(inImg);
+    return outImg;
+    //return [NSData dataWithBytes:&outImg length:outlen];
+} //nii_demosaic()
+
+unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    size_t lineBytes = hdr->dim[1] * hdr->bitpix/8;
+    if ((hdr->datatype == DT_RGB24) && (hdr->bitpix == 24)) {
+        lineBytes = hdr->dim[1];
+        dim3to7 = dim3to7 * 3;
+    } //rgb data saved planar (RRR..RGGGG..GBBB..B
+    
+    //printf("smoking gun %d %d\n",hdr->dim[3],hdr->dim[4]); return bImg;
+    
+    unsigned char line[lineBytes];
+    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
+        size_t slBottom = (size_t)sl*sliceBytes;
+        size_t slTop = (((size_t)sl+1)*sliceBytes)-lineBytes;
+        for (int y = 0; y < halfY; y++) {
+            //swap order of lines
+            memcpy(&line, &bImg[slBottom], lineBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], lineBytes);
+            memcpy(&bImg[slTop], &line, lineBytes);
+            slTop -= lineBytes;
+            slBottom += lineBytes;
+        } //for y
+    } //for each slice
+    return bImg;
+} //nii_flipImgY()
+
+unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
+    //DICOM row order opposite from NIfTI
+    int halfZ = hdr->dim[3] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
+    if (halfZ < 1) return bImg;
+    int dim4to7 = 1;
+    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];
+    unsigned char slice[sliceBytes];
+    
+    for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice
+        long long slBottom = vol*volBytes;
+        long long slTop = ((vol+1)*volBytes)-sliceBytes;
+        for (int z = 0; z < halfZ; z++) {
+            //swap order of lines
+            memcpy(&slice, &bImg[slBottom], sliceBytes);
+            memcpy(&bImg[slBottom], &bImg[slTop], sliceBytes);
+            memcpy(&bImg[slTop], &slice, sliceBytes);
+            slTop -= sliceBytes;
+            slBottom += sliceBytes;
+        } //for Z
+    } //for each volume
+    return bImg;
+} //nii_flipImgZ()
+
+unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
+    //flip slice order
+    if (h->dim[3] < 2) return bImg;
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0,0,h->dim[3]-1);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipZ;
+    LOAD_MAT33(mFlipZ,1.0f, 0.0f, 0.0f, 0.0f,1.0f,0.0f, 0.0f,0.0f,-1.0f);
+    s= nifti_mat33_mul( s , mFlipZ );
+    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);
+    return nii_flipImgZ(bImg,h);
+}//nii_flipZ()
+
+
+unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
+    mat33 s;
+    mat44 Q44;
+    LOAD_MAT33(s,h->srow_x[0],h->srow_x[1],h->srow_x[2], h->srow_y[0],h->srow_y[1],h->srow_y[2],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2]);
+    LOAD_MAT44(Q44,h->srow_x[0],h->srow_x[1],h->srow_x[2],h->srow_x[3],
+               h->srow_y[0],h->srow_y[1],h->srow_y[2],h->srow_y[3],
+               h->srow_z[0],h->srow_z[1],h->srow_z[2],h->srow_z[3]);
+    vec4 v= setVec4(0,h->dim[2]-1,0);
+    v = nifti_vect44mat44_mul(v, Q44); //after flip this voxel will be the origin
+    mat33 mFlipY;
+    LOAD_MAT33(mFlipY,1.0f, 0.0f, 0.0f, 0.0f,-1.0f,0.0f, 0.0f,0.0f,1.0f);
+    
+    s= nifti_mat33_mul( s , mFlipY );
+    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);
+    return nii_flipImgY(bImg,h);
+}//nii_flipY()
+
+unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr) {
+    size_t imgsz = nii_ImgBytes(hdr);
+    FILE *file = fopen(imgname , "rb");
+	if (!file) {
+         printf("Error: unable to open %s\n", imgname);
+         return NULL;
+    }
+	fseek(file, 0, SEEK_END);
+	long long fileLen=ftell(file);
+    if (fileLen < (imgsz+hdr.vox_offset)) return NULL;
+	fseek(file, hdr.vox_offset, SEEK_SET);
+    unsigned char *bImg = (unsigned char *)malloc(imgsz);
+	fread(bImg, imgsz, 1, file);
+	fclose(file);
+    return bImg;
+} //nii_loadImg()
+
+unsigned char * nii_rgb2Planar(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar) {
+    //DICOM data saved in triples RGBRGBRGB, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+    if (hdr->datatype != DT_RGB24) return bImg;
+    if (isPlanar == 1) return bImg;//return nii_bgr2rgb(bImg,hdr);
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
+    int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
+    unsigned char  slice24[ sliceBytes24 ];
+    int sliceOffsetR = 0;
+    for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+        memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
+        int sliceOffsetG = sliceOffsetR + sliceBytes8;
+        int sliceOffsetB = sliceOffsetR + 2*sliceBytes8;
+        int i = 0;
+        int j = 0;
+        for (int rgb = 0; rgb < sliceBytes8; rgb++) {
+            bImg[sliceOffsetR+j] =slice24[i];
+            i++;
+            bImg[sliceOffsetG+j] =slice24[i];
+            i++;
+            bImg[sliceOffsetB+j] =slice24[i];
+            i++;
+            j++;
+        }
+        sliceOffsetR += sliceBytes24;
+    } //for each slice
+    return bImg;
+} //nii_rgb2Planar()
+
+unsigned char * nii_iVaries(unsigned char *img, struct nifti_1_header *hdr){
+    //each DICOM image can have its own intesity scaling, whereas NIfTI requires the same scaling for all images in a file
+    //WARNING: do this BEFORE nii_check16bitUnsigned!!!!
+    //if (hdr->datatype != DT_INT16) return img;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return img;
+    float * img32=(float*)malloc(nVox*sizeof(float));
+    if (hdr->datatype == DT_UINT8) {
+        uint8_t * img8i = (uint8_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img8i[i];
+    } else if (hdr->datatype == DT_UINT16) {
+        uint16_t * img16ui = (uint16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16ui[i];
+    } else if (hdr->datatype == DT_INT16) {
+        int16_t * img16i = (int16_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img16i[i];
+    } else if (hdr->datatype == DT_INT32) {
+        int32_t * img32i = (int32_t*) img;
+        for (int i=0; i < nVox; i++)
+            img32[i] = img32i[i];
+    }
+    free (img); //release previous image
+    for (int i=0; i < nVox; i++)
+        img32[i] = (img32[i]* hdr->scl_slope)+hdr->scl_inter;
+    hdr->scl_slope = 1;
+    hdr->scl_inter = 0;
+    hdr->datatype = DT_FLOAT;
+    hdr->bitpix = 32;
+    return (unsigned char*) img32;
+} //nii_iVaries()
+
+unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, int seqRepeats) {
+    //Philips can save time as 3rd dimensions, NIFTI requires time is 4th dimension
+    int dim4to7 = 1;
+    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;
+#ifdef myUseCOut
+    std::cout<<"Converting XYTZ to XYZT with "<<hdr->dim[3]<<" slices (Z) and "<<  dim4to7<< "volumes" <<std::endl;
+    if ((dim4to7 % seqRepeats) != 0) {
+        std::cout<<"Error: patient position repeats "<<seqRepeats<<"  times, but this does not evenly divide number of volumes: "<<  dim4to7 <<std::endl;
+        seqRepeats = 1;
+    }
+    
+#else
+    printf("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)", seqRepeats,dim4to7);
+        seqRepeats = 1;
+    }
+    
+#endif
+    uint64_t typeRepeats = dim4to7 / seqRepeats;
+    uint64_t sliceBytes = hdr->dim[1]*hdr->dim[2]*hdr->bitpix/8;
+    uint64_t seqBytes = sliceBytes * seqRepeats;
+    uint64_t typeBytes = seqBytes * hdr->dim[3];
+    
+    uint64_t imgSz = nii_ImgBytes(*hdr);
+    //this uses a lot of RAM, someday this could be done in place...
+    unsigned char *outImg = (unsigned char *)malloc(imgSz);
+    //memcpy(&tempImg[0], &bImg[0], imgSz);
+    uint64_t origPos = 0;
+    uint64_t Pos = 0; //
+    
+    for (int t = 0; t < typeRepeats; t++) { //for each volume
+        for (int s = 0; s < seqRepeats; s++) {
+            origPos = (t*typeBytes) +s*sliceBytes;
+            for (int z = 0; z < hdr->dim[3]; z++) { //for each slice
+                memcpy( &outImg[Pos],&bImg[origPos], sliceBytes);
+                Pos += sliceBytes;
+                origPos += seqBytes;
+            }
+        }//for s
+    }
+    free(bImg);
+    return outImg;
+} //nii_ImgBytes()
+
+unsigned char * nii_byteswap(unsigned char *img, struct nifti_1_header *hdr){
+    if (hdr->bitpix < 9) return img;
+    uint64_t nvox = nii_ImgBytes(*hdr) / (hdr->bitpix/8);
+    void *ar = (void*) img;
+    if (hdr->bitpix == 16) nifti_swap_2bytes( nvox , ar );
+    if (hdr->bitpix == 32) nifti_swap_4bytes( nvox , ar );
+    if (hdr->bitpix == 64) nifti_swap_8bytes( nvox , ar );
+    return img;
+} //nii_byteswap()
+
+unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries) {
+    //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img
+
+    if (headerDcm2Nii(dcm, hdr) == EXIT_FAILURE) return NULL;
+    unsigned char * img = nii_loadImgCore(imgname, *hdr);
+    if (img == NULL) return img;
+#ifdef __BIG_ENDIAN__
+    if ((dcm.isLittleEndian) && (hdr->bitpix > 8))
+        img = nii_byteswap(img, hdr);
+#else
+    if ((!dcm.isLittleEndian) && (hdr->bitpix > 8))
+        img = nii_byteswap(img, hdr);
+#endif
+    if (hdr->datatype ==DT_RGB24) img = nii_rgb2Planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped
+    if (dcm.CSA.mosaicSlices > 1) {
+        img = nii_demosaic(img, hdr, dcm.CSA.mosaicSlices, dcm.CSA.protocolSliceNumber1);
+        /* we will do this in nii_dicom_batch #ifdef obsolete_mosaic_flip
+         img = nii_flipImgY(img, hdr);
+         #endif*/
+    }
+    if (iVaries) img = nii_iVaries(img, hdr);
+    int nAcq = dcm.locationsInAcquisition;
+    if ((nAcq > 1) && (hdr->dim[0] < 4) && ((hdr->dim[3]%nAcq)==0) && (hdr->dim[3]>nAcq) ) {
+        hdr->dim[4] = hdr->dim[3]/nAcq;
+        hdr->dim[3] = nAcq;
+        hdr->dim[0] = 4;
+    }
+    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);
+    return img;
+} //nii_loadImgX()
+
+struct TDICOMdata readDICOMv(char * fname, bool isVerbose) {
+    struct TDICOMdata d = clear_dicom_data();
+    strcpy(d.protocolName, ""); //fill dummy with empty space so we can detect kProtocolNameGE
+    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
+		
+		return d;
+	}
+	fseek(file, 0, SEEK_END);
+	long 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
+		return d;
+	}
+	fseek(file, 0, SEEK_SET);
+	//Allocate memory
+	unsigned char *buffer=(unsigned char *)malloc(fileLen+1);
+	if (!buffer) {
+		printf( "Memory error!");
+        fclose(file);
+		return d;
+	}
+	//Read file contents into buffer
+	fread(buffer, fileLen, 1, file);
+	fclose(file);
+    if ((buffer[128] != 'D') || (buffer[129] != 'I')  || (buffer[130] != 'C') || (buffer[131] != 'M')) {
+        free (buffer);
+        return d;
+    }
+    //DEFINE DICOM TAGS
+#define  kUnused 0x0001+(0x0001 << 16 )
+#define  kStart 0x0002+(0x0000 << 16 )
+#define  kTransferSyntax 0x0002+(0x0010 << 16)
+#define  kStudyDate 0x0008+(0x0020 << 16 )
+#define  kStudyTime 0x0008+(0x0030 << 16 )
+#define  kAcquisitionTime 0x0008+(0x0032 << 16 )
+#define  kManufacturer 0x0008+(0x0070 << 16 )
+#define  kProtocolNameGE 0x0008+(0x103E << 16 )
+#define  kComplexImageComponent 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent'
+#define  kPatientName 0x0010+(0x0010 << 16 )
+#define  kPatientID 0x0010+(0x0020 << 16 )
+#define  kMRAcquisitionType 0x0018+(0x0023 << 16)
+#define  kZThick  0x0018+(0x0050 << 16 )
+#define  kTR  0x0018+(0x0080 << 16 )
+#define  kTE  0x0018+(0x0081 << 16 )
+#define  kEchoNum  0x0018+(0x0086 << 16 ) //IS
+#define  kZSpacing  0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices'
+#define  kProtocolName  0x0018+(0x1030<< 16 )
+#define  kInPlanePhaseEncodingDirection  0x0018+(0x1312<< 16 ) //CS
+#define  kPatientOrient  0x0018+(0x5100<< 16 )    //0018,5100. patient orientation - 'HFS'
+    //#define  kDiffusionBFactorSiemens  0x0019+(0x100C<< 16 ) //   0019;000C;SIEMENS MR HEADER  ;B_value                         ;1;IS;1
+#define  kLastScanLoc  0x0019+(0x101B<< 16 )
+#define  kDiffusionDirectionGEX  0x0019+(0x10BB<< 16 ) //DS
+#define  kDiffusionDirectionGEY  0x0019+(0x10BC<< 16 ) //DS
+#define  kDiffusionDirectionGEZ  0x0019+(0x10BD<< 16 ) //DS
+#define  kPatientPosition 0x0020+(0x0032 << 16 )
+#define  kSeriesNum 0x0020+(0x0011 << 16 )
+#define  kAcquNum 0x0020+(0x0012 << 16 )
+#define  kImageNum 0x0020+(0x0013 << 16 )
+#define  kOrientation 0x0020+(0x0037 << 16 )
+#define  kImageComments 0x0020+(0x4000<< 16 )// '0020' '4000' 'LT' 'ImageComments'
+#define  kLocationsInAcquisitionGE 0x0021+(0x104F<< 16 )// 'SS' 'LocationsInAcquisitionGE'
+#define  kSamplesPerPixel 0x0028+(0x0002 << 16 )
+#define  kPlanarRGB 0x0028+(0x0006 << 16 )
+#define  kDim3 0x0028+(0x0008 << 16 ) //number of frames - for Philips this is Dim3*Dim4
+#define  kDim2 0x0028+(0x0010 << 16 )
+#define  kDim1 0x0028+(0x0011 << 16 )
+#define  kXYSpacing  0x0028+(0x0030 << 16 ) //'0028' '0030' 'DS' 'PixelSpacing'
+#define  kBitsAllocated 0x0028+(0x0100 << 16 )
+#define  kBitsStored 0x0028+(0x0101 << 16 )//'0028' '0101' 'US' 'BitsStored'
+#define  kIsSigned 0x0028+(0x0103 << 16 )
+#define  kIntercept 0x0028+(0x1052 << 16 )
+#define  kSlope 0x0028+(0x1053 << 16 )
+#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  kDiffusionBFactorGE  0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9
+#define  kCoilSiemens  0x0051+(0x100F << 16 )
+#define  kLocationsInAcquisition  0x0054+(0x0081 << 16 )
+#define  kIconImageSequence 0x0088+(0x0200 << 16 )
+#define  kDiffusionBFactor  0x2001+(0x1003 << 16 )// FL
+#define  kSliceOrient  0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
+#define  kLocationsInAcquisitionPhilips  0x2001+(0x1018 << 16 )
+#define  kNumberOfDynamicScans  0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans'
+#define  kMRAcquisitionTypePhilips 0x2005+(0x106F << 16)
+#define  kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP'
+#define  kAngulationFH 0x2005+(0x1072 << 16)//'2005' '1072' 'FL' 'MRStackAngulationFH'
+#define  kAngulationRL 0x2005+(0x1073 << 16)//'2005' '1073' 'FL' 'MRStackAngulationRL'
+#define  kMRStackOffcentreAP 0x2005+(0x1078 << 16)
+#define  kMRStackOffcentreFH 0x2005+(0x1079 << 16)
+#define  kMRStackOffcentreRL 0x2005+(0x107A << 16)
+#define  kDiffusionDirectionRL 0x2005+(0x10B0 << 16)
+#define  kDiffusionDirectionAP 0x2005+(0x10B1 << 16)
+#define  kDiffusionDirectionFH 0x2005+(0x10B2 << 16)
+#define  k2005140F 0x2005+(0x140F << 16)
+#define  kWaveformSq 0x5400+(0x0100 << 16)
+#define  kImageStart 0x7FE0+(0x0010 << 16 )
+#define  kNest 0xFFFE +(0xE000 << 16 ) //Item follows SQ
+#define  kUnnest 0xFFFE +(0xE00D << 16 ) //ItemDelimitationItem [length defined] http://www.dabsoft.ch/dicom/5/7.5/
+#define  kUnnest2 0xFFFE +(0xE0DD << 16 )//SequenceDelimitationItem [length undefined]
+    double zSpacing = -1.0l; //includes slice thickness plus gap
+    int locationsInAcquisitionGE = 0; int locationsInAcquisitionPhilips = 0;
+    long lPos = 128+4; //4-byte signature starts at 128
+    uint32_t lLength;
+    uint32_t 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
+    char vr[2];
+    bool isIconImageSequence = false;
+    bool isSwitchToImplicitVR = false;
+    bool isSwitchToBigEndian = false;
+    //bool geiisBug = false; //for buggy GEIIS http://forum.dcmtk.org/viewtopic.php?p=7162&sid=3b516cc751aae51fbb5e73184abe37c2
+    bool is2005140FSQ = false; //for buggy Philips
+    bool is2005140FSQwarned = false; //for buggy Philips
+    bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice
+    int patientPositionCount = 0;
+    long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP)
+    long echoNum = 0;
+    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);
+        else
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        if ((isSwitchToBigEndian) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToBigEndian = false;
+            d.isLittleEndian = false;
+            groupElement = buffer[lPos+1] | (buffer[lPos] << 8) | (buffer[lPos+3] << 16) | (buffer[lPos+2] << 24);
+        }//transfer syntax requests switching endian after group 0002
+        if ((isSwitchToImplicitVR) && ((groupElement & 0xFFFF) != 2)) {
+            isSwitchToImplicitVR = false;
+            d.isExplicitVR = false;
+        } //transfer syntax requests switching VR after group 0001
+        //uint32_t group = (groupElement & 0xFFFF);
+        lPos += 4;
+        if ((groupElement == kNest) || (groupElement == kUnnest) || (groupElement == kUnnest2)) {
+            vr[0] = 'N';
+            vr[1] = 'A';
+            
+            //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail
+            //printf("xxx");
+            lLength = 4;
+        } else if (d.isExplicitVR) {
+            vr[0] = buffer[lPos]; vr[1] = buffer[lPos+1];
+            if (buffer[lPos+1] < 'A') {//implicit vr with 32-bit length
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos += 4;
+            } else if ( ((buffer[lPos] == 'U') && (buffer[lPos+1] == 'N'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'B'))
+                       || ((buffer[lPos] == 'O') && (buffer[lPos+1] == 'W'))
+                       ) { //VR= UN, OB, OW, SQ  || ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q'))
+                lPos = lPos + 4;  //skip 2 byte VR string and 2 reserved bytes = 4 bytes
+                if (d.isLittleEndian)
+                    lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+                lPos = lPos + 4;  //skip 4 byte length
+            } else if   ((buffer[lPos] == 'S') && (buffer[lPos+1] == 'Q')) {
+                lLength = 8; //Sequence Tag
+                is2005140FSQ = (groupElement == k2005140F);
+            } else { //explicit VR with 16-bit length
+                if ((d.isLittleEndian)  )
+                    lLength = buffer[lPos+2] | (buffer[lPos+3] << 8);
+                else
+                    lLength = buffer[lPos+3] | (buffer[lPos+2] << 8);
+                lPos += 4;  //skip 2 byte VR string and 2 length bytes = 4 bytes
+            }
+        } else { //implicit VR
+            if (d.isLittleEndian)
+                lLength = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
+            else
+                lLength = buffer[lPos+3] | (buffer[lPos+2] << 8) | (buffer[lPos+1] << 16) | (buffer[lPos] << 24);
+            lPos += 4;  //we have loaded the 32-bit length
+        } //if explicit else implicit VR
+        if (lLength == 0xFFFFFFFF) lLength = 8; //SQ (Sequences) use 0xFFFFFFFF [4294967295] to denote unknown length
+        //next: look for required tags
+        if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions
+        switch ( groupElement ) {
+            case 	kTransferSyntax: {
+                char transferSyntax[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], transferSyntax);
+                //printf("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.2") == 0)
+                    isSwitchToBigEndian = true; //isExplicitVR=true;
+                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'\n",transferSyntax);
+#endif
+                    
+                    d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
+                }
+                break;} //{} provide scope for variable 'transferSyntax
+            case 	kStudyDate:
+                dcmStr (lLength, &buffer[lPos], d.studyDate);
+                break;
+            case 	kManufacturer:
+                d.manufacturer = dcmStrManufacturer (lLength, &buffer[lPos]);
+                break;
+            case 	kComplexImageComponent:
+                d.isHasPhase = (buffer[lPos]=='P') && (toupper(buffer[lPos+1]) == 'H');
+                d.isHasMagnitude = (buffer[lPos]=='M') && (toupper(buffer[lPos+1]) == 'A');
+                break;
+            case 	kAcquisitionTime : {
+                char acquisitionTimeTxt[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt);
+                d.acquisitionTime = atof(acquisitionTimeTxt);
+                
+                break; }
+            case 	kStudyTime :
+                dcmStr (lLength, &buffer[lPos], d.studyTime);
+                break;
+            case 	kPatientName :
+                dcmStr (lLength, &buffer[lPos], d.patientName);
+                break;
+            case 	kPatientID :
+                dcmStr (lLength, &buffer[lPos], d.patientID);
+                break;
+            case 	kProtocolNameGE: {
+                if (d.manufacturer == kMANUFACTURER_GE)
+                    dcmStr (lLength, &buffer[lPos], d.protocolName);
+                break; }
+                
+            case 	kProtocolName : {
+                if (strlen(d.protocolName) < 1) //GE uses a generic session name here: do not overwrite kProtocolNameGE
+                    dcmStr (lLength, &buffer[lPos], d.protocolName);
+                break; }
+            case 	kPatientOrient :
+                dcmStr (lLength, &buffer[lPos], d.patientOrient);
+                break;
+            case 	kLastScanLoc :
+                d.lastScanLoc = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+                /*case kDiffusionBFactorSiemens :
+                 if (d.manufacturer == kMANUFACTURER_SIEMENS)
+                 printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos]));
+                 
+                 break;*/
+            case kDiffusionDirectionGEX :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[0][1] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEY :
+                if (d.manufacturer == kMANUFACTURER_GE)  d.CSA.dtiV[0][2] =  dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kDiffusionDirectionGEZ :
+                if (d.manufacturer == kMANUFACTURER_GE) {
+                    d.CSA.dtiV[0][3] =  dcmStrFloat(lLength, &buffer[lPos]);
+                    d.CSA.numDti = 1;
+                }
+                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
+                    
+                    is2005140FSQwarned = true;
+                } else {
+                    patientPositionCount++;
+                    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
+                        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;
+                        } //if different position from 1st slice in file
+                    } //if not first slice in file
+                } //not after 2005,140F
+                break;
+            case 	kInPlanePhaseEncodingDirection:
+                d.phaseEncodingRC = toupper(buffer[lPos]); //first character is either 'R'ow or 'C'ol
+                break;
+            case 	kSeriesNum:
+                d.seriesNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kAcquNum:
+                d.acquNum = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kImageNum:
+                d.imageNum = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kPlanarRGB:
+                d.isPlanarRGB = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim3:
+                d.xyzDim[3] = dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kSamplesPerPixel:
+                d.samplesPerPixel = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim2:
+                d.xyzDim[2] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kDim1:
+                d.xyzDim[1] = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kXYSpacing:
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 2, d.xyzMM);
+                
+                break;
+            case 	kImageComments:
+                dcmStr (lLength, &buffer[lPos], d.imageComments);
+                break;
+            case 	kLocationsInAcquisitionGE:
+                locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsAllocated :
+                d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kBitsStored :
+                d.bitsStored = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kIsSigned : //http://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html
+                d.isSigned = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+                break;
+            case 	kTR :
+                d.TR = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kTE :
+                d.TE = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kEchoNum :
+                echoNum =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case 	kZSpacing :
+                zSpacing = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kSlope :
+                d.intenScale = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kIntercept :
+                d.intenIntercept = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case 	kZThick :
+                d.xyzMM[3] = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+                
+            case 	kCoilSiemens : {
+                if (d.manufacturer == kMANUFACTURER_SIEMENS) {
+                    //see if image from single coil "H12" or an array "HEA;HEP"
+                    char coilStr[kDICOMStr];
+                    dcmStr (lLength, &buffer[lPos], coilStr);
+                    //long coilNum = 0;
+                    char *ptr;
+                    dcmStrDigitsOnly(coilStr);
+                    coilNum = strtol(coilStr, &ptr, 10);
+                    if (*ptr != '\0')
+                        coilNum = 0;
+                }
+                break; }
+            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 	kNumberOfDynamicScans:
+                d.numberOfDynamicScans =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            case	kMRAcquisitionType:
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                break;
+            case	kMRAcquisitionTypePhilips: //kMRAcquisitionType
+                if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
+                break;
+            case	kAngulationRL:
+                d.angulation[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationAP:
+                d.angulation[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kAngulationFH:
+                d.angulation[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreRL:
+                d.stackOffcentre[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreAP:
+                d.stackOffcentre[2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kMRStackOffcentreFH:
+                d.stackOffcentre[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kSliceOrient: {
+                char orientStr[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], orientStr);
+                if (toupper(orientStr[0])== 'S')
+                    d.sliceOrient = kSliceOrientSag; //sagittal
+                else if (toupper(orientStr[0])== 'C')
+                    d.sliceOrient = kSliceOrientCor; //coronal
+                else
+                    d.sliceOrient = kSliceOrientTra; //transverse (axial)
+                break; }
+            case	kDiffusionBFactor:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
+                    d.CSA.numDti++; //increment with BFactor: on Philips slices with B=0 have B-factor but no diffusion directions
+                    if ((d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                        d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                }
+                break;
+            case    kDiffusionDirectionRL:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case kDiffusionDirectionAP:
+                if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
+                    d.CSA.dtiV[d.CSA.numDti-1][2] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                break;
+            case	kDiffusionDirectionFH:
+                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);
+                //http://www.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:DICOM_for_DWI_and_DTI
+                break;
+            case 	kWaveformSq:
+                d.imageStart = 1; //abort!!!
+#ifdef myUseCOut
+                std::cout<<"Warning: Unable to extract sound wave forms" <<std::endl;
+#else
+                printf("Warning: Unable to extract sound wave forms\n");
+#endif
+                break;
+            case 	kCSAImageHeaderInfo:
+                readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose);
+                break;
+                //case kObjectGraphics:
+                //    printf("---->%d,",lLength);
+                //    break;
+            case kDiffusionBFactorGE :
+                if (d.manufacturer == kMANUFACTURER_GE) d.CSA.dtiV[0][0] =  dcmStrInt(lLength, &buffer[lPos]);
+                break;
+            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
+                    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 	kOrientation :
+                dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient);
+                break;
+            case 	kImageStart:
+                //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
+                if (!isIconImageSequence) //do not exit for proprietary thumbnails
+                    d.imageStart = (int)lPos;
+                //geiisBug = false;
+                isIconImageSequence = false;
+                break;
+        } //switch/case for groupElement
+        //printf("VR=%c%c tag=%04x,%04x length=%lu, pos=%ld x=%d\n",vr[0],vr[1],groupElement & 65535,groupElement>>16, lLength, lPos, d.xyzDim[1]);
+        lPos = lPos + (lLength);
+    }
+    free (buffer);
+    d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime);
+    //printf("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) && (d.locationsInAcquisition == 0))
+            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!
+                    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");
+                            d.xyzMM[2] = d.xyzMM[1];
+                        }
+    if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) {
+        printf("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");
+        d.xyzMM[3] = 1.0;
+    }
+    if (coilNum > 0) //segment images with multiple coils
+        d.seriesNum = d.seriesNum + (100*coilNum);
+        if (echoNum > 2) //segment images with multiple echoes
+            d.seriesNum = d.seriesNum + (100*echoNum);
+            if (isVerbose) {
+                printf("Patient Position %f %f %f\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
+                printf("DICOM 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\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);
+            }
+    return d;
+} // readDICOM()
+
+struct TDICOMdata readDICOM(char * fname) {
+    return readDICOMv(fname, false);
+}
+
+
diff --git a/wxWidgets/nii_dicom_batch.c b/wxWidgets/nii_dicom_batch.c
new file mode 100644
index 0000000..53991c3
--- /dev/null
+++ b/wxWidgets/nii_dicom_batch.c
@@ -0,0 +1,1475 @@
+//#define myNoSave //do not save images to disk
+
+#ifndef myDisableZLib
+ #include <zlib.h>
+ #ifndef myDisableTarGz 
+ // #include "untgz.h"
+ #endif
+#endif
+#ifdef myUseCOut
+ #include <iostream>
+#endif
+#include "nifti1_io_core.h"
+#include "nifti1.h"
+#include "nii_dicom_batch.h"
+#include "nii_dicom.h"
+#include "tinydir.h"
+#include <ctype.h> //toupper
+#include <float.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
+#include "nii_ortho.h"
+#if defined(_WIN64) || defined(_WIN32)
+#include <windows.h> //write to registry
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+//gcc -O3 -o main main.c nii_dicom.c
+#if defined(_WIN64) || defined(_WIN32)
+const char kPathSeparator ='\\';
+const char kFileSep[2] = "\\";
+#else
+const char kPathSeparator ='/';
+const char kFileSep[2] = "/";
+#endif
+
+struct TDCMsort {
+    uint64_t indx, img;
+};
+
+struct TSearchList {
+    unsigned long numItems, maxItems;
+    char **str;
+};
+
+void dropFilenameFromPath(char *path) { //
+   const char *dirPath = strrchr(path, '/'); //UNIX
+   if (dirPath == 0)
+      dirPath = strrchr(path, '\\'); //Windows
+    if (dirPath == NULL) {
+        strcpy(path,"");
+    } else
+        path[dirPath - path] = 0; // please make sure there is enough space in TargetDirectory
+}
+
+void getFileName( char *pathParent, const char *path) //if path is c:\d1\d2 then filename is 'd2'
+{
+    const char *filename = strrchr(path, '/'); //UNIX
+    if (filename == 0)
+       filename = strrchr(path, '\\'); //Windows
+    //const char *filename = strrchr(path, kPathSeparator); //x
+    if (filename == NULL) {//no path separator
+        strcpy(pathParent,path);
+        return;
+    }
+    filename++;
+    strcpy(pathParent,filename);
+}
+
+bool is_fileexists(const char * filename) {
+    FILE * fp = NULL;
+    if ((fp = fopen(filename, "r"))) {
+        fclose(fp);
+        return true;
+    }
+    return false;
+}
+
+bool is_fileNotDir(const char* path) { //returns false if path is a folder; requires #include <sys/stat.h>
+    struct stat buf;
+    stat(path, &buf);
+    return S_ISREG(buf.st_mode);
+} //is_file()
+
+bool is_exe(const char* path) { //requires #include <sys/stat.h>
+    struct stat buf;
+    stat(path, &buf);
+    return (S_ISREG(buf.st_mode) && (buf.st_mode & 0111) );
+} //is_exe()
+
+int is_dir(const char *pathname, int follow_link) {
+struct stat s;
+if ((NULL == pathname) || (0 == strlen(pathname)))
+	return 0;
+int err = stat(pathname, &s);
+if(-1 == err) {
+        return 0; /* does not exist */
+} else {
+    if(S_ISDIR(s.st_mode)) {
+       return 1; /* it's a dir */
+    } else {
+        return 0;/* exists but is no dir */
+    }
+}
+} //is_dir
+/*int is_dir(const char *pathname, int follow_link) {
+    //http://sources.gentoo.org/cgi-bin/viewvc.cgi/path-sandbox/trunk/libsbutil/get_tmp_dir.c?revision=260
+	struct stat buf;
+	int retval;
+	if ((NULL == pathname) || (0 == strlen(pathname)))
+		return 0;
+	retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
+	if ((-1 != retval) && (S_ISDIR(buf.st_mode)))
+		return 1;
+	if ((-1 == retval) && (ENOENT != errno)) // Some or other error occurred
+		return -1;
+	return 0;
+} //is_dir()
+*/
+
+bool isDICOMfile(const char * fname) {
+    FILE *fp = fopen(fname, "rb");
+	if (!fp)  return false;
+	fseek(fp, 0, SEEK_END);
+	long long fileLen=ftell(fp);
+    if (fileLen < 256) return false;
+	fseek(fp, 0, SEEK_SET);
+	unsigned char buffer[256];
+	fread(buffer, 256, 1, fp);
+	fclose(fp);
+    if ((buffer[128] != 'D') || (buffer[129] != 'I')  || (buffer[130] != 'C') || (buffer[131] != 'M'))
+        return false;
+    return true;
+} //isDICOMfile()
+
+void geCorrectBvecs(struct TDICOMdata *d, int sliceDir){
+    //0018,1312 phase encoding is either in row or column direction
+    //0043,1039 (or 0043,a039). b value (as the first number in the string).
+    //0019,10bb (or 0019,a0bb). phase diffusion direction
+    //0019,10bc (or 0019,a0bc). frequency diffusion direction
+    //0019,10bd (or 0019,a0bd). slice diffusion direction
+    //These directions are relative to freq,phase,slice, so although no
+    //transformations are required, you need to check the direction of the
+    //phase encoding. This is in DICOM message 0018,1312. If this has value
+    //COL then if swap the x and y value and reverse the sign on the z value.
+    //If the phase encoding is not COL, then just reverse the sign on the x value.
+    if (d->manufacturer != kMANUFACTURER_GE) return;
+    if (d->CSA.numDti < 1) return;
+    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;
+    }
+    bool col = false;
+    if (d->phaseEncodingRC== 'C')
+        col = true;
+    else if (d->phaseEncodingRC!= 'R') {
+        #ifdef myUseCOut
+     	std::cout<<"Error: Unable to determine DTI gradients, 0018,1312 should be either R or C" <<std::endl;
+    	#else
+        printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C");
+        #endif
+        return;
+    }
+    if (abs(sliceDir) != 3)
+            #ifdef myUseCOut
+     	std::cout<<"GE DTI gradients only tested for axial acquisitions" <<std::endl;
+    	#else
+        printf("GE DTI gradients only tested for axial acquisitions");
+        #endif
+    //printf("GE row(0) or column(1) = %d",col);
+    #ifdef myUseCOut
+    std::cout<<"Reorienting "<< d->CSA.numDti << "GE DTI gradients. Please validate if you are conducting DTI analyses. isCol="<<col <<std::endl;
+    #else
+    printf("Reorienting %d GE DTI gradients. Please validate if you are conducting DTI analyses. isCol=%d\n", d->CSA.numDti, col);
+    #endif
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }
+        d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][1];
+        if (!col) { //rows need to be swizzled
+            float swap = d->CSA.dtiV[i][1];
+            d->CSA.dtiV[i][1] = -d->CSA.dtiV[i][2];
+            d->CSA.dtiV[i][2] = swap;
+        }
+        if (sliceDir < 0)
+                d->CSA.dtiV[i][3] = -d->CSA.dtiV[i][3];
+        if (isSameFloat(d->CSA.dtiV[i][1],-0)) d->CSA.dtiV[i][1] = 0.0f;
+        if (isSameFloat(d->CSA.dtiV[i][2],-0)) d->CSA.dtiV[i][2] = 0.0f;
+        if (isSameFloat(d->CSA.dtiV[i][3],-0)) d->CSA.dtiV[i][3] = 0.0f;
+        
+    }
+} //geCorrectBvecs()
+
+/*void philipsCorrectBvecs(struct TDICOMdata *d){
+    //Philips DICOM data stored in patient (LPH) space, regardless of settings in Philips user interface
+    //algorithm from PARtoNRRD/CATNAP (with July 20, 2007 patch) http://godzilla.kennedykrieger.org/~jfarrell/software_web.htm
+    //0018,5100. patient orientation - 'HFS'
+    //2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
+    //2005,1071 MRStackAngulationAP
+    //2005,1072 MRStackAngulationFH
+    //2005,1073 MRStackAngulationRL
+    if (d->manufacturer != kMANUFACTURER_PHILIPS) return;
+    if (d->CSA.numDti < 1) return;
+    mat33 tpp,tpo;
+    if ((toupper(d->patientOrient[0])== 'F') && (toupper(d->patientOrient[1])== 'F'))
+        LOAD_MAT33(tpp, 0,-1,0, -1,0,0, 0,0,1); //feet first
+    else if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F'))
+        LOAD_MAT33(tpp, 0,1,0,-1,0,0, 0,0,-1); //head first
+    else {
+        printf("Unable to correct Philips DTI vectors: patient position must be head or feet first\n");
+        return;
+    }
+    //unused? mat33 rev_tpp =nifti_mat33_transpose(tpp);
+    if (toupper(d->patientOrient[2])== 'S')//supine
+        LOAD_MAT33 (tpo, 1,0,0, 0,1,0, 0,0,1);
+    else if (toupper(d->patientOrient[2])== 'P')//prone
+        LOAD_MAT33 (tpo,-1,0,0, 0,-1,0, 0,0,1);
+    else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'R'))   //DR
+        LOAD_MAT33 (tpo,0,-1,0, 1,0,0, 0,0,1);
+    else if ((toupper(d->patientOrient[2])== 'D') && (toupper(d->patientOrient[3])== 'L'))//DL
+        LOAD_MAT33 (tpo,0,1,0, -1,0,0, 0,0,1);
+    else {
+        printf("DTI vector error: Position is not HFS,HFP,HFDR,HFDL,FFS,FFP,FFDR, or FFDL: %s\n",d->patientOrient);
+        return;
+    }
+    //unused? mat33 rev_tpo =nifti_mat33_transpose(tpo);
+    //unused? mat33 tpom = nifti_mat33_mul( tpo, tpp);
+    //unused? mat33 rev_tpom = nifti_mat33_mul( rev_tpp,rev_tpo  );
+    printf("Reorienting %d Philip DTI gradients with angulations %f %f %f. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti, d->angulation[1], d->angulation[2], d->angulation[3]);
+    
+    float rl = d->angulation[1]  * M_PI /180; //as radian
+    float ap = d->angulation[2]  * M_PI /180;
+    float fh = d->angulation[3]  * M_PI /180;
+    //printf(" %f %f %f \n",ap,fh,rl);
+    mat33 trl, tap, tfh, rev_tsom, dtiextra;
+    LOAD_MAT33 (trl,1,0,0,  0, cos(rl),- sin(rl),  0, sin(rl),cos(rl));
+    LOAD_MAT33 (tap, cos(ap),0, sin(ap),  0,1,0,                 - sin(ap),0, cos(ap));
+    LOAD_MAT33 (tfh,cos(fh),- sin(fh),0, sin(fh), cos(fh),0,    0,0,1);
+    mat33 rev_trl =nifti_mat33_transpose(trl);
+    mat33 rev_tap =nifti_mat33_transpose(tap);
+    mat33 rev_tfh =nifti_mat33_transpose(tfh);
+    mat33 mtemp1 = nifti_mat33_mul( trl, tap);
+    //unused? mat33 tang = nifti_mat33_mul( mtemp1, tfh);
+    mtemp1 = nifti_mat33_mul( rev_tfh, rev_tap );
+    mat33 rev_tang = nifti_mat33_mul( mtemp1, rev_trl);
+    //kSliceOrientSag
+    if (d->sliceOrient == kSliceOrientSag)//SAGITTAL
+        LOAD_MAT33 (rev_tsom, 0,0,1,  0,-1,0, -1,0,0 );
+    else if (d->sliceOrient == 2)//CORONAL
+        LOAD_MAT33 (rev_tsom, 0,0,1,  -1,0,0, 0,1,0 );
+    else
+        LOAD_MAT33 (rev_tsom, 0,-1,0,  -1,0,0, 0,0,1 );
+    LOAD_MAT33 (dtiextra, 0,-1,0,  -1,0,0, 0,0,1 );
+    mat33 mtemp2 = nifti_mat33_mul( dtiextra, rev_tsom);
+    mtemp1 = nifti_mat33_mul( mtemp2, rev_tang);
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        //printf("%d\tvin=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]);
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }
+        vec3 v3;
+        
+        for (int v= 1; v < 4; v++) //normalize and reverse vector directions
+            v3.v[v-1] =-d->CSA.dtiV[i][v]/vLen;
+        v3 = nifti_vect33mat33_mul(v3,mtemp1);
+        v3 = nifti_vect33_norm(v3);
+        d->CSA.dtiV[i][1] = v3.v[0];
+        d->CSA.dtiV[i][2] = v3.v[1]; //NIfTI Y reversed relative to DICOM
+        d->CSA.dtiV[i][3] = v3.v[2];
+        //printf("%d\tvoutt=[\t%f\t%f\t%f\t]; bval=\t%f\n",i,d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3],d->CSA.dtiV[i][0]);
+        
+    }
+    if ( d->sliceOrient != kSliceOrientTra)
+        printf("Warning: Philips DTI gradients only evaluated for axial (transverse) acquisitions. Please verify sign and direction\n");
+} //philipsCorrectBvecs()
+*/
+
+void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir){
+    //see Matthew Robson's  http://users.fmrib.ox.ac.uk/~robson/internal/Dicom2Nifti111.m
+    //convert DTI vectors from scanner coordinates to image frame of reference
+    //Uses 6 orient values from ImageOrientationPatient  (0020,0037)
+    // requires PatientPosition 0018,5100 is HFS (head first supine)
+    if ((d->manufacturer != kMANUFACTURER_SIEMENS) && (d->manufacturer != kMANUFACTURER_PHILIPS)) return;
+    if (d->CSA.numDti < 1) return;
+    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
+        return;
+    }
+    vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]);
+    vec3 phase_vector = setVec3(d->orient[4],d->orient[5],d->orient[6]);
+    vec3 slice_vector = crossProduct(read_vector ,phase_vector);
+    read_vector = nifti_vect33_norm(read_vector);
+    phase_vector = nifti_vect33_norm(phase_vector);
+    slice_vector = nifti_vect33_norm(slice_vector);
+    for (int i = 0; i < d->CSA.numDti; i++) {
+        float vLen = sqrt( (d->CSA.dtiV[i][1]*d->CSA.dtiV[i][1])
+                          + (d->CSA.dtiV[i][2]*d->CSA.dtiV[i][2])
+                          + (d->CSA.dtiV[i][3]*d->CSA.dtiV[i][3]));
+        if ((d->CSA.dtiV[i][0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
+            for (int v= 0; v < 4; v++)
+                d->CSA.dtiV[i][v] =0.0f;
+            continue; //do not normalize or reorient b0 vectors
+        }//if bvalue=0
+        vec3 bvecs_old =setVec3(d->CSA.dtiV[i][1],d->CSA.dtiV[i][2],d->CSA.dtiV[i][3]);
+        vec3 bvecs_new =setVec3(dotProduct(bvecs_old,read_vector),dotProduct(bvecs_old,phase_vector),dotProduct(bvecs_old,slice_vector) );
+        bvecs_new = nifti_vect33_norm(bvecs_new);
+        d->CSA.dtiV[i][1] = bvecs_new.v[0];
+        d->CSA.dtiV[i][2] = -bvecs_new.v[1];
+        d->CSA.dtiV[i][3] = bvecs_new.v[2];
+        if (sliceDir == kSliceOrientMosaicNegativeDeterminant) d->CSA.dtiV[i][2] = -d->CSA.dtiV[i][2];
+        for (int v= 0; v < 4; v++)
+            if (d->CSA.dtiV[i][v] == -0.0f) d->CSA.dtiV[i][v] = 0.0f; //remove sign from values that are virtually zero
+    } //for each direction
+    #ifdef myUseCOut
+    if (sliceDir == kSliceOrientMosaicNegativeDeterminant)
+        std::cout<<"WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal)."<<std::endl;
+    else if ( d->sliceOrient == kSliceOrientTra)
+    	std::cout<<"Saving "<<d->CSA.numDti<<" DTI gradients. Please validate if you are conducting DTI analyses."<<std::endl;
+    else
+    	std::cout<<"WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files."<<std::endl;
+    #else
+    if (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");
+	#endif
+} //siemensPhilipsCorrectBvecs()
+
+bool isSamePosition (struct TDICOMdata d, struct TDICOMdata d2){
+    if (!isSameFloat(d.patientPosition[1],d2.patientPosition[1])) return false;
+    if (!isSameFloat(d.patientPosition[2],d2.patientPosition[2])) return false;
+    if (!isSameFloat(d.patientPosition[3],d2.patientPosition[3])) return false;
+    return true;
+} //isSamePosition()
+
+bool nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir) {
+    //reports true if last volume is excluded (e.g. philip stores an ADC map)
+    //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) {//extract DTI from different slices
+        numDti = 0;
+        for (int i = 0; i < nConvert; i++) { //for each image
+            if ((dcmList[indx0].CSA.mosaicSlices > 1)  || (isSamePosition(dcmList[indx0],dcmList[dcmSort[i].indx]))) {
+                if (numDti < kMaxDTIv) 
+                    for (int v = 0; v < 4; v++) //for each vector+B-value
+                        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;
+    }
+    if (numDti < 3) return false;
+    if (numDti > kMaxDTIv) {
+    	#ifdef myUseCOut
+    	std::cout<<"Error: more than "<<kMaxDTIv<<" DTI directions detected (check for a new software)"<<std::endl;
+		#else
+        printf("Error: more than %d DTI directions detected (check for a new software)", kMaxDTIv);
+        #endif
+        return false;
+    }
+    bool bValueVaries = false;
+    for (int i = 1; i < numDti; i++) //check if all bvalues match first volume
+        if (dcmList[indx0].CSA.dtiV[i][0] != dcmList[indx0].CSA.dtiV[0][0]) bValueVaries = true;
+    if (!bValueVaries) {
+        for (int i = 1; i < numDti; i++)
+                printf("bxyz %g %g %g %g\n",dcmList[indx0].CSA.dtiV[i][0],dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]);
+        printf("Error: only one B-value reported for all volumes: %g\n",dcmList[indx0].CSA.dtiV[0][0]);
+        return false;
+    }
+        
+    int firstB0 = -1;
+    for (int i = 0; i < numDti; i++) //check if all bvalues match first volume
+        if (isSameFloat(dcmList[indx0].CSA.dtiV[i][0],0) ) {
+            firstB0 = i;
+            break;
+        }
+    //printf("2015ALPHA %d -> %d\n",numDti, nConvert);
+    #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
+    bool isFinalADC = false;
+    /*if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)
+     && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f))
+     )
+     printf("xxx-->%f\n", dcmList[indx0].CSA.dtiV[numDti-1][3]);*/
+    if ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)
+        && (!isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][0],0.0f))  //not a B-0 image
+        && ((isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][1],0.0f)) ||
+            (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][2],0.0f)) ||
+            (isSameFloat(dcmList[indx0].CSA.dtiV[numDti-1][3],0.0f)) )) {//yet all vectors are zero!!!! must be ADC
+            isFinalADC = true; //final volume is ADC map
+            numDti --; //remove final volume - it is a computed ADC map!
+            dcmList[indx0].CSA.numDti = numDti;
+        }
+    // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution
+    geCorrectBvecs(&dcmList[indx0],sliceDir);
+    siemensPhilipsCorrectBvecs(&dcmList[indx0],sliceDir);
+    if (opts.isVerbose) {
+        for (int i = 0; i < (numDti-1); i++) {
+        	#ifdef myUseCOut
+    		std::cout<<i<<"\tB=\t"<<dcmList[indx0].CSA.dtiV[i][0]<<"\tVec=\t"<<
+                   dcmList[indx0].CSA.dtiV[i][1]<<"\t"<<dcmList[indx0].CSA.dtiV[i][2]
+                   <<"\t"<<dcmList[indx0].CSA.dtiV[i][3]<<std::endl;
+			#else
+            printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, dcmList[indx0].CSA.dtiV[i][0],
+                   dcmList[indx0].CSA.dtiV[i][1],dcmList[indx0].CSA.dtiV[i][2],dcmList[indx0].CSA.dtiV[i][3]);
+            
+        	#endif
+        } //for each direction
+    }
+    if (!opts.isFlipY ) { //!FLIP_Y&& (dcmList[indx0].CSA.mosaicSlices < 2) mosaics are always flipped in the Y direction
+        for (int i = 0; i < (numDti-1); i++) {
+            if (fabs(dcmList[indx0].CSA.dtiV[i][2]) > FLT_EPSILON)
+                dcmList[indx0].CSA.dtiV[i][2] = -dcmList[indx0].CSA.dtiV[i][2];
+        } //for each direction
+    } //if not a mosaic
+    //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]);
+    char txtname[2048] = {""};
+    strcpy (txtname,pathoutname);
+    strcat (txtname,".bval");
+    //printf("Saving DTI %s\n",txtname);
+    FILE *fp = fopen(txtname, "w");
+    if (fp == NULL) return isFinalADC;
+    for (int i = 0; i < (numDti-1); i++)
+        fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][0]);
+    fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][0]);
+    fclose(fp);
+    strcpy(txtname,pathoutname);
+    strcat (txtname,".bvec");
+    //printf("Saving DTI %s\n",txtname);
+    fp = fopen(txtname, "w");
+    if (fp == NULL) return isFinalADC;
+    for (int v = 1; v < 4; v++) {
+        for (int i = 0; i < (numDti-1); i++)
+            fprintf(fp, "%g\t", dcmList[indx0].CSA.dtiV[i][v]);
+        fprintf(fp, "%g\n", dcmList[indx0].CSA.dtiV[numDti-1][v]);
+    }
+    fclose(fp);
+    return isFinalADC;
+} //nii_SaveDTI()
+
+float sqr(float v){
+    return v*v;
+}  //sqr()
+
+float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) {
+    //some MRI scans have gaps between slices, some CT have overlapping slices. Comparing adjacent slices provides measure for dx between slices
+    return sqrt( sqr(d1.patientPosition[1]-d2.patientPosition[1])+
+                sqr(d1.patientPosition[2]-d2.patientPosition[2])+
+                sqr(d1.patientPosition[3]-d2.patientPosition[3]));
+} //intersliceDistance()
+
+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;
+    struct TDCMsort dcmSortIn[nConvert];
+    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);
+            dcmSort[k] = dcmSortIn[i];
+            i++;
+        }
+} //swapDim3Dim4()
+
+bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){
+    //detect whether some DICOM images report different intensity scaling
+    //some Siemens PET scanners generate 16-bit images where slice has its own scaling factor.
+    // since NIfTI provides a single scaling factor for each file, these images require special consideration
+    if (nConvert < 2) return false;
+    bool iVaries = false;
+    float iScale = dcmList[dcmSort[0].indx].intenScale;
+    float iInter = dcmList[dcmSort[0].indx].intenIntercept;
+    for (int i = 1; i < nConvert; i++) { //stack additional images
+        uint64_t indx = dcmSort[i].indx;
+        if (fabs (dcmList[indx].intenScale - iScale) > FLT_EPSILON) iVaries = true;
+        if (fabs (dcmList[indx].intenIntercept- iInter) > FLT_EPSILON) iVaries = true;
+    }
+    return iVaries;
+} //intensityScaleVaries()
+
+
+/*unsigned char * nii_bgr2rgb(unsigned char* bImg, struct nifti_1_header *hdr) {
+ //DICOM planarappears to be BBB..B,GGG..G,RRR..R, NIfTI RGB saved in planes RRR..RGGG..GBBBB..B
+ //  see http://www.barre.nom.fr/medical/samples/index.html US-RGB-8-epicard
+ if (hdr->datatype != DT_RGB24) return bImg;
+ int dim3to7 = 1;
+ for (int i = 3; i < 8; i++)
+ if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+ int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
+ int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
+ //Byte bImg[ bSz ];
+ //[img getBytes:&bImg length:bSz];
+ unsigned char slice24[sliceBytes24];
+ int sliceOffsetR = 0;
+ for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
+ memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8*2], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[sliceBytes8], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ memcpy( &bImg[sliceOffsetR], &slice24[0], sliceBytes8);
+ sliceOffsetR += sliceBytes8;
+ } //for each slice
+ return bImg;
+ } //nii_ImgBytes()*/
+
+bool niiExists(const char*pathoutname) {
+    char niiname[2048] = {""};
+    strcat (niiname,pathoutname);
+    strcat (niiname,".nii");
+    if (is_fileexists(niiname)) return true;
+    char gzname[2048] = {""};
+    strcat (gzname,pathoutname);
+    strcat (gzname,".nii.gz");
+    if (is_fileexists(gzname)) return true;
+    return false;
+} //niiExists()
+
+int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts) {
+    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) {
+            	#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
+                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
+            }
+        }
+     }
+    char inname[1024] = {""};//{"test%t_%av"}; //% a = acquisition, %n patient name, %t time
+    strcpy(inname, opts.filename);
+    char outname[1024] = {""};
+    char newstr[256];
+    if (strlen(inname) < 1) {
+        strcpy(inname, "T%t_N%n_S%s");
+    }
+    int start = 0;
+    int pos = 0;
+    while (pos<strlen(inname)) {
+        if (inname[pos] == '%') {
+            if (pos > start) {
+                strncpy(&newstr[0], &inname[0] + start, pos - start);
+                newstr[pos - start] = '\0';
+                strcat (outname,newstr);
+            }
+            pos++; //extra increment: skip both % and following character
+            char f = 'P';
+            if (pos<strlen(inname)) f = toupper(inname[pos]);
+            if (f == 'C')
+                strcat (outname,dcm.imageComments);
+            if (f == 'F')
+                strcat (outname,opts.indirParent);
+            if (f == 'I')
+                strcat (outname,dcm.patientID);
+            if (f == 'N')
+                strcat (outname,dcm.patientName);
+            if (f == 'P')
+                strcat (outname,dcm.protocolName);
+            if ((f >= '0') && (f <= '9')) {
+                if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
+                    char zeroPad[12] = {""};
+                    sprintf(zeroPad,"%%0%dd",atoi(&f));
+                    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);
+            }
+            if (f == 'T') {
+                sprintf(newstr, "%0.0f", dcm.dateTime);
+                strcat (outname,newstr);
+            }
+            start = pos + 1;
+        } //found a % character
+        pos++;
+    } //for each character in input
+    if (pos > start) { //append any trailing characters
+        strncpy(&newstr[0], &inname[0] + start, pos - start);
+        newstr[pos - start] = '\0';
+        strcat (outname,newstr);
+    }
+    if (strlen(outname) < 1) strcpy(outname, "dcm2nii_invalidName");
+    if (outname[0] == '.') outname[0] = '_'; //make sure not a hidden file
+    //eliminate illegal characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+    for (int pos = 0; pos<strlen(outname); pos ++)
+        if ((outname[pos] == '<') || (outname[pos] == '>') || (outname[pos] == ':')
+            || (outname[pos] == '"') || (outname[pos] == '\\') || (outname[pos] == '/')
+             
+            || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?'))
+            outname[pos] = '_';
+    //printf("name=*%s* %d %d\n", outname, pos,start);
+    char baseoutname[2048] = {""};
+    strcat (baseoutname,pth);
+    char appendChar[2] = {"a"};
+    appendChar[0] = kPathSeparator;
+    if (pth[strlen(pth)-1] != kPathSeparator)
+        strcat (baseoutname,appendChar);
+    strcat (baseoutname,outname);
+    char pathoutname[2048] = {""};
+    strcat (pathoutname,baseoutname);
+    int i = 0;
+    while (niiExists(pathoutname) && (i < 26)) {
+        strcpy(pathoutname,baseoutname);
+        appendChar[0] = 'a'+i;
+        strcat (pathoutname,appendChar);
+        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
+        return EXIT_FAILURE;
+    }
+    //printf("-->%s\n",pathoutname); return EXIT_SUCCESS;
+    //printf("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();
+    strcpy(opts.indirParent,"myFolder");
+    char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"};
+    nii_createFilename(dcm, niiFilenameBase, opts) ;
+    strcpy(niiFilename,"Example output filename: '");
+    strcat(niiFilename,niiFilenameBase);
+    if (opts.isGz)
+        strcat(niiFilename,".nii.gz'");        
+    else
+        strcat(niiFilename,".nii'");
+} //nii_createDummyFilename()
+
+#ifndef myDisableZLib
+void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src_buffer, unsigned long src_len) {
+    //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] = {""};
+    strcpy (fname,baseName);
+    strcat (fname,".nii.gz");
+    unsigned long hdrPadBytes = sizeof(hdr) + 4; //348 byte header + 4 byte pad
+    unsigned long cmp_len = compressBound(src_len+hdrPadBytes);
+    unsigned char *pCmp = (unsigned char *)malloc(cmp_len);
+    z_stream strm;
+    uLong file_crc32 = crc32(0L, Z_NULL, 0);
+    //strm.adler = crc32(0L, Z_NULL, 0);
+    strm.total_in = 0;
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    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) return;
+    //add header
+    unsigned char *pHdr = (unsigned char *)malloc(hdrPadBytes);
+    memcpy(pHdr,&hdr, sizeof(hdr));
+    strm.avail_in = (unsigned int)hdrPadBytes; // size of input
+    strm.next_in = (Bytef *)pHdr; // input header
+    deflate(&strm, Z_NO_FLUSH);
+    file_crc32 = crc32(file_crc32, pHdr, (unsigned int)hdrPadBytes);
+    //add image
+    strm.avail_in = (unsigned int)src_len; // size of input
+    strm.next_in = (Bytef *)src_buffer; // input image
+    deflate(&strm, Z_FINISH); //Z_NO_FLUSH;
+    //finish up
+    deflateEnd(&strm);
+    file_crc32 = crc32(file_crc32, src_buffer, (unsigned int)src_len);
+    cmp_len = strm.total_out;
+    if (cmp_len <= 0) return;
+    FILE *fileGz = fopen(fname, "wb");
+    if (!fileGz) return;
+    //write header http://www.gzip.org/zlib/rfc-gzip.html
+    fputc((char)0x1f, fileGz); //ID1
+    fputc((char)0x8b, fileGz); //ID2
+    fputc((char)0x08, fileGz); //CM - use deflate compression method
+    fputc((char)0x00, fileGz); //FLG - no addition fields
+    fputc((char)0x00, fileGz); //MTIME0
+    fputc((char)0x00, fileGz); //MTIME1
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //MTIME2
+    fputc((char)0x00, fileGz); //XFL
+    fputc((char)0xff, fileGz); //OS
+    //write Z-compressed data
+    fwrite (&pCmp[2] , sizeof(char), cmp_len-6, fileGz); //-6 as LZ78 format has 2 bytes header and 4 bytes tail
+    //write tail: write redundancy check and uncompressed size as bytes to ensure LITTLE-ENDIAN order
+    fputc((unsigned char)(file_crc32), fileGz);
+    fputc((unsigned char)(file_crc32 >> 8), fileGz);
+    fputc((unsigned char)(file_crc32 >> 16), fileGz);
+    fputc((unsigned char)(file_crc32 >> 24), fileGz);
+    fputc((unsigned char)(strm.total_in), fileGz);
+    fputc((unsigned char)(strm.total_in >> 8), fileGz);
+    fputc((unsigned char)(strm.total_in >> 16), fileGz);
+    fputc((unsigned char)(strm.total_in >> 24), fileGz);
+    fclose(fileGz);
+    free(pCmp);
+} //writeNiiGz()
+#endif
+
+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);
+    #ifndef myDisableZLib
+        if  ((opts.isGz) &&  (strlen(opts.pigzname)  < 1) &&  ((imgsz+hdr.vox_offset) <  2147483647) ) { //use internal compressor
+        writeNiiGz (niiFilename, hdr,  im,imgsz);
+        return EXIT_SUCCESS;
+    }
+    #endif
+    char fname[2048] = {""};
+    strcpy (fname,niiFilename);
+    strcat (fname,".nii");
+    FILE *fp = fopen(fname, "wb");
+    if (!fp) return EXIT_FAILURE;
+    fwrite(&hdr, sizeof(hdr), 1, fp);
+    uint32_t pad = 0;
+    fwrite(&pad, sizeof( pad), 1, fp);
+    fwrite(&im[0], imgsz, 1, fp);
+    fclose(fp);
+    if ((opts.isGz) &&  (strlen(opts.pigzname)  > 0) ) {
+    	char command[768];
+    	strcpy(command, "\"" );
+        strcat(command, opts.pigzname );
+        strcat(command, "\" \"");
+        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)
+    	PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
+    	STARTUPINFO StartupInfo; //This is an [in] parameter
+    	ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+    	StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
+    	if(CreateProcess(NULL, command, NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL, NULL,&StartupInfo,&ProcessInfo)) { 
+        	WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+        	CloseHandle(ProcessInfo.hThread);
+        	CloseHandle(ProcessInfo.hProcess);
+    	} else
+    	#ifdef myUseCOut
+    	std::cout<<"compression failed "<<command<<std::endl;
+		#else
+		printf("compression failed %s\n",command);
+    	#endif
+    	#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
+    }
+    return EXIT_SUCCESS;
+} //nii_saveNII()
+
+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;
+    int nVol = 1;
+    for (int i = 4; i < 8; i++) {
+        if (hdr.dim[i] > 1) nVol = nVol * hdr.dim[i];
+        hdr1.dim[i] = 0;
+    }
+    hdr1.dim[0] = 3; //save as 3D file
+    size_t imgsz = nii_ImgBytes(hdr1);
+    size_t pos = 0;
+    char fname[2048] = {""};
+    char zeroPad[1024] = {""};
+    int zeroPadLen = (1 + log10(nVol));
+    sprintf(zeroPad,"%%s_%%0%dd",zeroPadLen);
+    for (int i = 1; i <= nVol; i++) {
+        sprintf(fname,zeroPad,niiFilename,i);
+        if (nii_saveNII(fname, hdr1, (unsigned char*)&im[pos], opts) == EXIT_FAILURE) return EXIT_FAILURE;
+        pos += imgsz;
+    }
+    return EXIT_SUCCESS;
+} //nii_saveNII3D
+
+
+
+void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){
+    //default NIfTI 16-bit is signed, set to unusual 16-bit unsigned if required...
+    if (hdr->datatype != DT_UINT16) return;
+    int dim3to7 = 1;
+    for (int i = 3; i < 8; i++)
+        if (hdr->dim[i] > 1) dim3to7 = dim3to7 * hdr->dim[i];
+    int nVox = hdr->dim[1]*hdr->dim[2]* dim3to7;
+    if (nVox < 1) return;
+    unsigned short * img16 = (unsigned short*) img;
+    unsigned short max16 = img16[0];
+    //clock_t start = clock();
+    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)
+            #ifdef myUseCOut
+    	std::cout<<"Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion"<<std::endl;
+		#else
+        printf("Note: intensity range requires saving as rare 16-bit UNSIGNED integer. Subsequent tools may require 32-bit conversion\n");
+    	#endif
+    else
+        hdr->datatype = DT_INT16;
+} //nii_check16bitUnsigned()
+
+int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]) {
+    //Siemens CT bug: when a user draws an open object graphics object onto a 2D slice this is appended as an additional image,
+    //regardless of slice position. These images do not report number of positions in the volume, so we need tedious leg work to detect
+
+    uint64_t indx0 = dcmSort[0].indx;
+    if ((nConvert < 2) ||(dcmList[indx0].manufacturer != kMANUFACTURER_SIEMENS) || (!isSameFloat(dcmList[indx0].TR ,0.0f))) return nConvert;
+    float prevDx = 0.0;
+    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
+            return i;
+        }
+        prevDx = dx;
+    }
+    return nConvert; //all images in sequential order
+}
+
+
+int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts) {
+    bool iVaries = intensityScaleVaries(nConvert,dcmSort,dcmList);
+    uint64_t indx = dcmSort[0].indx;
+    uint64_t indx0 = dcmSort[0].indx;
+    bool saveAs3D = dcmList[indx].isHasPhase;
+    struct nifti_1_header hdr0;
+    unsigned char * img = nii_loadImgX(nameList->str[indx], &hdr0,dcmList[indx], iVaries);
+    if (opts.isVerbose)
+    #ifdef myUseCOut
+    	std::cout<<"Converting "<<nameList->str[indx]<<std::endl;
+		#else
+        printf("Converting %s\n",nameList->str[indx]);
+        #endif
+    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);
+
+    
+    /*if ((nConvert < 2) && (dcmList[indx].locationsInAcquisition > 0)) { //stack philips 4D file
+        
+        int nAcq = dcmList[indx].locationsInAcquisition;
+        if ((hdr0.dim[0] < 4) && ((hdr0.dim[3]%nAcq)==0)) {
+            hdr0.dim[4] = hdr0.dim[3]/nAcq;
+            hdr0.dim[3] = nAcq;
+            hdr0.dim[0] = 4;
+        }
+        if (hdr0.dim[0] > 3) {
+            if (dcmList[indx].patientPositionSequentialRepeats > 1) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension)
+                imgM = nii_XYTZ_XYZT(imgM, &hdr0,dcmList[indx].patientPositionSequentialRepeats );
+        }
+    }*/
+    //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) {
+        if (hdr0.dim[3] < 2) {
+            //stack volumes with multiple acquisitions
+            int nAcq = 1;
+            //Next line works in theory, but fails with Siemens CT that saves pairs of slices as acquisitions, see example "testSiemensStackAcq"
+            //  nAcq = 1+abs( dcmList[dcmSort[nConvert-1].indx].acquNum-dcmList[indx0].acquNum);
+            //therefore, the 'same position' is the most robust solution in the real world.
+            if ((dcmList[indx0].manufacturer == kMANUFACTURER_SIEMENS) && (isSameFloat(dcmList[indx0].TR ,0.0f))) {
+                nConvert = siemensCtKludge(nConvert, dcmSort,dcmList);
+            }
+            if ((nAcq == 1 ) && (dcmList[indx0].locationsInAcquisition > 0)) nAcq = nConvert/dcmList[indx0].locationsInAcquisition;
+            if (nAcq < 2 ) {
+                nAcq = 0;
+                for (int i = 0; i < nConvert; i++)
+                    if (isSamePosition(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx])) nAcq++;
+            }
+            /*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);
+                //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) ) {
+                hdr0.dim[3] = nConvert/nAcq;
+                hdr0.dim[4] = nAcq;
+                hdr0.dim[0] = 4;
+            } else
+                hdr0.dim[3] = nConvert;
+            float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]);
+            if ((hdr0.dim[4] > 0) && (dx ==0) && (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS)) {
+                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);
+            }
+            dcmList[dcmSort[0].indx].xyzMM[3] = dx; //16Sept2014 : correct DICOM for true distance between slice centers:
+            // e.g. MCBI Siemens ToF 0018:0088 reports 16mm SpacingBetweenSlices, but actually 0.5mm
+            if (dx > 0) hdr0.pixdim[3] = dx;
+        } else if (hdr0.dim[4] < 2) {
+            hdr0.dim[4] = nConvert;
+            hdr0.dim[0] = 4;
+        } else {
+            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]);
+        struct nifti_1_header hdrI;
+        for (int i = 1; i < nConvert; i++) { //stack additional images
+            indx = dcmSort[i].indx;
+            //if (headerDcm2Nii(dcmList[indx], &hdrI) == EXIT_FAILURE) return EXIT_FAILURE;
+            img = nii_loadImgX(nameList->str[indx], &hdrI, dcmList[indx],iVaries);
+            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
+                return EXIT_FAILURE;
+            }
+            memcpy(&imgM[(uint64_t)i*imgsz], &img[0], imgsz);
+            free(img);
+        }
+    }
+
+    //printf("Mango %zd\n", (imgsz* nConvert)); return 0;
+    char pathoutname[2048] = {""};
+    if (nii_createFilename(dcmList[dcmSort[0].indx], pathoutname, opts) == EXIT_FAILURE) return EXIT_FAILURE;
+    if (strlen(pathoutname) <1) return EXIT_FAILURE;
+    int sliceDir = 0;
+    if (hdr0.dim[3] > 1)
+        sliceDir =headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
+    if (sliceDir < 0) {
+        imgM = nii_flipZ(imgM, &hdr0);
+        sliceDir = abs(sliceDir);
+    }
+    bool isFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir);
+    isFinalADC =isFinalADC; //simply to silence compiler warning when myNoSave defined
+
+    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
+    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
+    else
+        fflush(stdout); //GUI buffers printf, display all results
+    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));
+    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
+#ifndef myNoSave
+    if ((hdr0.dim[4] > 1) && (saveAs3D))
+        nii_saveNII3D(pathoutname, hdr0, imgM,opts);
+    else {
+        if ((isFinalADC) && (hdr0.dim[4] > 2)) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+            char pathoutnameADC[2048] = {""};
+            strcat(pathoutnameADC,pathoutname);
+            strcat(pathoutnameADC,"_ADC");
+            nii_saveNII(pathoutnameADC, hdr0, imgM, opts);
+            hdr0.dim[4] = hdr0.dim[4]-1;
+        };
+        nii_saveNII(pathoutname, hdr0, imgM, opts);
+    }
+#endif
+    free(imgM);
+    return EXIT_SUCCESS;
+} //saveDcm2Nii()
+
+int compareTDCMsort(void const *item1, void const *item2) {
+    //for quicksort http://blog.ablepear.com/2011/11/objective-c-tuesdays-sorting-arrays.html
+    struct TDCMsort const *dcm1 = (const struct TDCMsort *)item1;
+    struct TDCMsort const *dcm2 = (const struct TDCMsort *)item2;
+    if (dcm1->img < dcm2->img)
+        return -1;
+    else if (dcm1->img > dcm2->img)
+        return 1;
+    return 0; //tie
+} //compareTDCMsort()
+
+bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2) {
+    //returns true if d1 and d2 should be stacked together as a signle output
+    if (!d1.isValid) return false;
+    if (!d2.isValid) return false;
+    if ((d1.dateTime == d2.dateTime) && (d1.seriesNum == d2.seriesNum) && (d1.bitsAllocated == d2.bitsAllocated)
+        && (d1.xyzDim[1] == d2.xyzDim[1]) && (d1.xyzDim[2] == d2.xyzDim[2]) && (d1.xyzDim[3] == d2.xyzDim[3]) )
+        return true;
+    return false;
+} //isSameSet()
+
+void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) {
+    tinydir_dir dir;
+    tinydir_open(&dir, path);
+    while (dir.has_next) {
+        tinydir_file file;
+        tinydir_readfile(&dir, &file);
+        //printf("%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 (isDICOMfile(filename)) {
+            if (nameList->numItems < nameList->maxItems) {
+                nameList->str[nameList->numItems]  = (char *)malloc(strlen(filename)+1);
+                strcpy(nameList->str[nameList->numItems],filename);
+                //printf("OK\n");
+            }
+            nameList->numItems++;
+            //printf("dcm %lu %s \n",nameList->numItems, filename);
+        } else {
+        #ifdef MY_DEBUG
+            #ifdef myUseCOut
+                std::cout<<"Not a dicom"<< filename <<std::endl;
+            #else
+                printf("Not a dicom:\t%s\n", filename);
+            #endif
+        #endif
+        }
+        tinydir_next(&dir);
+    }
+    tinydir_close(&dir);
+} //searchDirForDICOM()
+
+int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    int nDuplicates = 0;
+    for (int i = 1; i < nConvert; i++) {
+        if (dcmSort[i].img == dcmSort[i-1].img)
+            nDuplicates ++;
+        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;
+} //removeDuplicates()
+
+int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSearchList *nameList){
+    //done AFTER sorting, so duplicates will be sequential
+    if (nConvert < 2) return nConvert;
+    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"<<nameList->str[dcmSort[i].indx] <<std::endl;
+		#else
+            printf("\t%s\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
+            #endif
+            nDuplicates ++;
+        }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;
+} //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 )
+        if(strcicmp(p_extension,ext) == 0) return true;
+    //if(strcmp(p_extension,ext) == 0) return true;
+    return false;
+} //isExt()
+
+int convert_parRec(struct TDCMopts opts) {
+    //sample dataset from Ed Gronenschild <ed.gronenschild at maastrichtuniversity.nl>
+    struct TSearchList nameList;
+    nameList.numItems = 1;
+    nameList.maxItems = 1;
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
+    nameList.str[0]  = (char *)malloc(strlen(opts.indir)+1);
+    strcpy(nameList.str[0],opts.indir);
+    dcmList[0] = nii_readParRec(nameList.str[0]);
+    struct TDCMsort dcmSort[1];
+    dcmSort[0].indx = 0;
+    saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts);
+    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 > 0)
+        for (int i = 0; i < nameList.numItems; i++)
+            free(nameList.str[i]);
+    free(nameList.str);
+
+    return EXIT_SUCCESS;
+} //convert_parRec()
+
+int nii_loadDir (struct TDCMopts* opts) {
+    //printf("-->%s",opts->filename);
+    //return EXIT_FAILURE;
+    if (strlen(opts->indir) < 1) {
+         #ifdef myUseCOut
+    	std::cout<<"No input"<<std::endl;
+		#else
+        printf("No input\n");
+        #endif
+        return EXIT_FAILURE;
+    }
+    #ifdef myUseCOut
+     std::cout << "Version  " <<kDCMvers <<std::endl; 
+    #else
+    printf("Version %s\n",kDCMvers);
+    #endif
+
+    
+    char indir[512];
+    strcpy(indir,opts->indir);
+    bool isFile = is_fileNotDir(opts->indir);
+    if (isFile) {//if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom
+        dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir);
+    }
+    if (strlen(opts->outdir) < 1)
+        strcpy(opts->outdir,opts->indir);
+    else if (!is_dir(opts->outdir,true)) {
+        #ifdef myUseCOut
+    	std::cout << "Warning: output folder invalid "<< opts->outdir<<" will try %s\n"<< opts->indir <<std::endl;
+    	#else
+     	printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir);
+        #endif
+        strcpy(opts->outdir,opts->indir);
+    }
+    /*if (isFile && ((isExt(indir, ".gz")) || (isExt(indir, ".tgz"))) ) {
+        #ifndef myDisableTarGz 
+         #ifndef myDisableZLib
+          untargz( indir, opts->outdir);
+         #endif
+        #endif
+    }*/
+    if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
+        strcpy(opts->indir, indir); //set to original file name, not path
+        return convert_parRec(*opts);
+    }
+
+    getFileName(opts->indirParent, opts->indir);
+    struct TSearchList nameList;
+    nameList.numItems = 0;
+    nameList.maxItems = 64000-1;
+    nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //we reserve one pointer (32 or 64 bits) per potential file
+    //1: find filenames of dicom files
+    searchDirForDICOM(opts->indir, &nameList,  5,1);
+    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
+        free(nameList.str);
+        return EXIT_FAILURE;
+    }
+    if (nameList.numItems < 1) {
+                #ifdef myUseCOut
+    	std::cout << "Overwhelmed: found more than "<<nameList.maxItems<<" DICOM images in " << opts->indir <<std::endl;
+    	#else
+        printf("Overwhelmed: found more than %lu DICOM images in %s\n",nameList.maxItems, opts->indir);
+        #endif
+        //goto freeMem;
+        return EXIT_FAILURE;
+    }
+    long long nDcm = nameList.numItems;
+    #ifdef myUseCOut
+    	std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl;
+    	#else
+    printf( "Found %lu DICOM images\n", nameList.numItems);
+    #endif
+    // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays
+    struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
+    for (int i = 0; i < nameList.numItems; i++ )
+        dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose);
+    //3: stack DICOMs with the same Series
+    int nConvertTotal = 0;
+    for (int i = 0; i < nDcm; i++ ) {
+        if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) {
+            int nConvert = 0;
+            for (int j = i; j < nDcm; j++)
+                if (isSameSet(dcmList[i],dcmList[j]) )
+                    nConvert ++;
+            struct TDCMsort dcmSort[nConvert];
+            nConvert = 0;
+            for (int j = i; j < nDcm; j++)
+                if (isSameSet(dcmList[i],dcmList[j]) ) {
+                    dcmSort[nConvert].indx = j;
+                    dcmSort[nConvert].img = ((uint64_t)dcmList[j].seriesNum << 32)+ dcmList[j].imageNum;
+                    dcmList[j].converted2NII = 1;
+                    nConvert ++;
+                }
+            qsort(dcmSort, nConvert, sizeof(struct TDCMsort), compareTDCMsort); //sort based on series and image numbers....
+            if (opts->isVerbose)
+                nConvert = removeDuplicatesVerbose(nConvert, dcmSort, &nameList);
+            else
+                nConvert = removeDuplicates(nConvert, dcmSort);
+            nConvertTotal += nConvert;
+            saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts);
+        }//convert all images of this series
+    }
+    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
+    if (nameList.numItems > 0)
+        for (int i = 0; i < nameList.numItems; i++)
+            free(nameList.str[i]);
+    free(nameList.str);
+    return EXIT_SUCCESS;
+} //nii_loadDir()
+
+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);
+		#else //myUseZLib
+ 		printf("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 
+    #else
+    strcpy(opts->pigzname,"/usr/local/bin/pigz");
+    char pigz[1024];
+    strcpy(pigz, opts->pigzname);
+    if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/bin/pigz");
+        if (!is_exe(opts->pigzname)) {
+        strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron");
+        if (!is_exe(opts->pigzname)) {
+            strcpy(opts->pigzname,argv[0]);
+            dropFilenameFromPath(opts->pigzname);//, opts.pigzname);
+            char appendChar[2] = {"a"};
+            appendChar[0] = kPathSeparator;
+            if (opts->pigzname[strlen(opts->pigzname)-1] != kPathSeparator) strcat (opts->pigzname,appendChar);
+            strcat(opts->pigzname,"pigz_mricron");
+            #if defined(_WIN64) || defined(_WIN32)
+            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);
+            	#else //myUseZLib
+                printf("Compression will be faster with %s\n",pigz);
+            	#endif
+            #endif
+                strcpy(opts->pigzname,"");
+            } //no pigz_mricron in exe's folder
+        } //no /usr/local/pigz_mricron
+       }//no /usr/bin/pigz
+    } //no /usr/local/pigz
+    #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[]) {
+    readFindPigz(opts, argv);
+    strcpy(opts->indir,"");
+    strcpy(opts->outdir,"");
+    opts->isGz = false;
+    opts->isFlipY = true;
+#ifdef myDebug
+    opts->isVerbose =   true;
+#else
+    opts->isVerbose = false;
+#endif
+    strcpy(opts->filename,"%f_%p_%t_%s");
+     HKEY  hKey;
+    DWORD vSize     = 0;
+    DWORD dwDataType = 0;
+    DWORD dwValue    = 0;
+    //RegOpenKeyEx(RegOpenKeyEx, key, 0, accessRights, keyHandle);
+    //if(RegOpenKeyEx(HKEY_CURRENT_USER,(WCHAR)"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+    if(RegOpenKeyExA(HKEY_CURRENT_USER,"Software\\dcm2nii", 0, KEY_QUERY_VALUE,&hKey) != ERROR_SUCCESS) {
+        RegCloseKey(hKey);
+        return;
+    }
+    vSize = sizeof(dwValue);
+    //if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, (&dwValue), &vSize) == ERROR_SUCCESS)
+    if(RegQueryValueExA(hKey,"isGZ", 0, (LPDWORD )&dwDataType, reinterpret_cast<BYTE *>(&dwValue), &vSize) == ERROR_SUCCESS)
+    	opts->isGz = dwValue;
+    vSize = 512;
+    char buffer[512];
+    if(RegQueryValueExA(hKey,"filename", 0,NULL,(LPBYTE)buffer,&vSize ) == ERROR_SUCCESS )
+ 	strcpy(opts->filename,buffer);
+ RegCloseKey(hKey); 
+} //readIniFile()
+
+#else
+//for Unix we will save preferences in a hidden text file in the home directory
+#define STATUSFILENAME "/.dcm2nii.ini"
+
+void readIniFile (struct TDCMopts *opts, const char * argv[]) {
+    readFindPigz(opts, argv);
+    sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME);
+    strcpy(opts->indir,"");
+    strcpy(opts->outdir,"");
+    opts->isGz = false;
+    opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
+#ifdef myDebug
+        opts->isVerbose =   true;
+#else
+        opts->isVerbose = false;
+#endif
+    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);
+        if ( strcmp(Setting,"isGZ") == 0 )
+            opts->isGz = atoi(Value);
+        else if ( strcmp(Setting,"filename") == 0 )
+            strcpy(opts->filename,Value);
+    }
+    fclose(fp);
+} //readIniFile()
+
+
+void saveIniFile (struct TDCMopts opts) {
+    FILE *fp = fopen(opts.optsname, "w");
+    //printf("%s\n",localfilename);
+    if (fp == NULL) return;
+    fprintf(fp, "isGZ=%d\n", opts.isGz);
+    fprintf(fp, "filename=%s\n", opts.filename);
+    fclose(fp);
+} //saveIniFile()
+
+#endif
+
+
+
+
diff --git a/wxWidgets/nii_ortho.c b/wxWidgets/nii_ortho.c
new file mode 100644
index 0000000..705a491
--- /dev/null
+++ b/wxWidgets/nii_ortho.c
@@ -0,0 +1,374 @@
+#include "nifti1.h"
+#include "nifti1_io_core.h"
+#include "nii_ortho.h"
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+#include <stddef.h>
+#include <float.h>
+#include <unistd.h>
+#include <stdio.h>
+//#define MY_DEBUG //verbose text reporting
+
+typedef struct  {
+    int v[3];
+} vec3i;
+
+mat33 matDotMul33 (mat33 a, mat33 b)
+// in Matlab: ret = a'.*b
+{
+    mat33 ret;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            ret.m[i][j] = a.m[i][j]*b.m[j][i];
+        }
+    }
+    return ret;
+}
+
+mat33 matMul33 (mat33 a, mat33 b)
+// mult = a * b
+{
+    mat33 mult;
+    for(int i=0;i<3;i++)
+    {
+        for(int j=0;j<3;j++)
+        {
+            mult.m[j][i]=0;
+            for(int k=0;k<3;k++)
+                mult.m[j][i]+=a.m[j][k]*b.m[k][i];
+        }
+    }
+    return mult;
+}
+
+float getOrthoResidual (mat33 orig, mat33 transform)
+{
+    mat33 mat = matDotMul33(orig, transform);
+    float ret = 0;
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            ret = ret + (mat.m[i][j]);
+        }
+    }
+    return ret;
+}
+
+mat33 getBestOrient(mat44 R, vec3i flipVec)
+//flipVec reports flip: [1 1 1]=no flips, [-1 1 1] flip X dimension
+{
+    mat33 ret, newmat, orig;
+    LOAD_MAT33(orig,R.m[0][0],R.m[0][1],R.m[0][2],
+               R.m[1][0],R.m[1][1],R.m[1][2],
+               R.m[2][0],R.m[2][1],R.m[2][2]);
+    float best = 0;//FLT_MAX;
+    float newval;
+    for (int rot = 0; rot < 6; rot++) { //6 rotations
+        switch (rot) {
+            case 0: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,flipVec.v[1],0, 0,0,flipVec.v[2]); break;
+            case 1: LOAD_MAT33(newmat,flipVec.v[0],0,0, 0,0,flipVec.v[1], 0,flipVec.v[2],0); break;
+            case 2: LOAD_MAT33(newmat,0,flipVec.v[0],0, flipVec.v[1],0,0, 0,0,flipVec.v[2]); break;
+            case 3: LOAD_MAT33(newmat,0,flipVec.v[0],0, 0,0,flipVec.v[1], flipVec.v[2],0,0); break;
+            case 4: LOAD_MAT33(newmat,0,0,flipVec.v[0], flipVec.v[1],0,0, 0,flipVec.v[2],0); break;
+            case 5: LOAD_MAT33(newmat,0,0,flipVec.v[0], 0,flipVec.v[1],0, flipVec.v[2],0,0); break;
+        }
+        newval = getOrthoResidual(orig, newmat);
+        if (newval > best) {
+            best = newval;
+            ret = newmat;
+        }
+    }
+    return ret;
+}
+
+bool isMat44Canonical(mat44 R)
+//returns true if diagonals >0 and all others =0
+//  no rotation is necessary - already in perfect orthogonal alignment
+{
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if ((i == j) && (R.m[i][j] <= 0) ) return false;
+            if ((i != j) && (R.m[i][j] != 0) ) return false;
+            
+        }//j
+    }//i
+    return true;
+}
+
+vec3i setOrientVec(mat33 m)
+// Assumes isOrthoMat NOT computed on INVERSE, hence return INVERSE of solution...
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    vec3i ret = {0, 0, 0};
+    //mat33 m = {-1,0,0, 0,1,0, 0,0,1};
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++) {
+            if (m.m[i][j] > 0) ret.v[j] = i+1;
+            if (m.m[i][j] < 0) ret.v[j] = -(i+1);
+        }//j
+    }//i
+    return ret;
+}
+
+mat44 setMat44Vec(mat33 m33, vec3 Translations)
+//convert a 3x3 rotation matrix to a 4x4 matrix where the last column stores translations and the last row is 0 0 0 1
+{
+    mat44 m44;
+    for (int i=0; i<3; i++) {
+        for (int j=0; j<3; j++) {
+            m44.m[i][j] = m33.m[i][j];
+        }
+    }
+    m44.m[0][3] = Translations.v[0];
+    m44.m[1][3] = Translations.v[1];
+    m44.m[2][3] = Translations.v[2];
+    m44.m[3][0] = 0;
+    m44.m[3][1] = 0;
+    m44.m[3][2] = 0;
+    m44.m[3][3] = 1;
+    return m44;
+}
+
+mat44 sFormMat(struct nifti_1_header *h) {
+    mat44 s;
+    s.m[0][0]=h->srow_x[0];
+    s.m[0][1]=h->srow_x[1];
+    s.m[0][2]=h->srow_x[2];
+    s.m[0][3]=h->srow_x[3];
+    s.m[1][0]=h->srow_y[0];
+    s.m[1][1]=h->srow_y[1];
+    s.m[1][2]=h->srow_y[2];
+    s.m[1][3]=h->srow_y[3];
+    s.m[2][0]=h->srow_z[0];
+    s.m[2][1]=h->srow_z[1];
+    s.m[2][2]=h->srow_z[2];
+    s.m[2][3]=h->srow_z[3];
+    s.m[3][0] = 0 ;
+    s.m[3][1] = 0 ;
+    s.m[3][2] = 0 ;
+    s.m[3][3] = 1 ;
+    return s;
+}
+
+void mat2sForm (struct nifti_1_header *h, mat44 s) {
+    h->srow_x[0] = s.m[0][0];
+    h->srow_x[1] = s.m[0][1];
+    h->srow_x[2] = s.m[0][2];
+    h->srow_x[3] = s.m[0][3];
+    h->srow_y[0] = s.m[1][0];
+    h->srow_y[1] = s.m[1][1];
+    h->srow_y[2] = s.m[1][2];
+    h->srow_y[3] = s.m[1][3];
+    h->srow_z[0] = s.m[2][0];
+    h->srow_z[1] = s.m[2][1];
+    h->srow_z[2] = s.m[2][2];
+    h->srow_z[3] = s.m[2][3];
+}
+
+size_t* orthoOffsetArray(int dim, int stepBytesPerVox) {
+    //return lookup table of length dim with values incremented by stepBytesPerVox
+    // e.g. if Dim=10 and stepBytes=2: 0,2,4..18, is stepBytes=-2 18,16,14...0
+    size_t *lut= (size_t *)malloc(dim*sizeof(size_t));
+    if (stepBytesPerVox > 0)
+        lut[0] = 0;
+    else
+        lut[0] = -stepBytesPerVox  *(dim-1);
+    if (dim > 1)
+        for (int i=1; i < dim; i++) lut[i] = lut[i-1] + (size_t)stepBytesPerVox;
+    return lut;
+} //orthoOffsetArray()
+
+//void  reOrientImg( unsigned char * restrict img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+void  reOrientImg( unsigned char *  img, vec3i outDim, vec3i outInc, int bytePerVox, int nvol) {
+    //reslice data to new orientation
+    //generate look up tables
+    size_t* xLUT =orthoOffsetArray(outDim.v[0], bytePerVox*outInc.v[0]);
+    size_t* yLUT =orthoOffsetArray(outDim.v[1], bytePerVox*outInc.v[1]);
+    size_t* zLUT =orthoOffsetArray(outDim.v[2], bytePerVox*outInc.v[2]);
+    //convert data
+    size_t bytePerVol = bytePerVox*outDim.v[0]*outDim.v[1]*outDim.v[2]; //number of voxels in spatial dimensions [1,2,3]
+    size_t o = 0; //output address
+    uint8_t *inbuf = (uint8_t *) malloc(bytePerVol); //we convert 1 volume at a time
+    uint8_t *outbuf = (uint8_t *) img; //source image
+    for (int vol= 0; vol < nvol; vol++) {
+        memcpy(&inbuf[0], &outbuf[vol*bytePerVol], bytePerVol); //copy source volume
+        for (int z = 0; z < outDim.v[2]; z++)
+            for (int y = 0; y < outDim.v[1]; y++) 
+                for (int x = 0; x < outDim.v[0]; x++) {
+                    memcpy(&outbuf[o], &inbuf[xLUT[x]+yLUT[y]+zLUT[z]], bytePerVox);
+                    o = o+ bytePerVox;
+                } //for each x
+    } //for each volume
+    //free arrays
+    free(inbuf); 
+    free(xLUT);
+    free(yLUT); 
+    free(zLUT); 
+} //reOrientImg
+
+unsigned char *  reOrient(unsigned char* img, struct nifti_1_header *h, vec3i orientVec, mat33 orient, vec3 minMM)
+//e.g. [-1,2,3] means reflect x axis, [2,1,3] means swap x and y dimensions
+{
+    size_t nvox = h->dim[1] * h->dim[2] * h->dim[3];
+    if (nvox < 1) return img;
+    vec3i outDim= {0,0,0};
+    vec3i outInc= {0,0,0};
+    for (int i = 0; i < 3; i++) { //set dimension, pixdim and 
+        outDim.v[i] =  h->dim[abs(orientVec.v[i])];
+        if (abs(orientVec.v[i]) == 1) outInc.v[i] = 1;
+        if (abs(orientVec.v[i]) == 2) outInc.v[i] = h->dim[1];
+        if (abs(orientVec.v[i]) == 3) outInc.v[i] = h->dim[1]*h->dim[2];
+        if (orientVec.v[i] < 0) outInc.v[i] = -outInc.v[i]; //flip
+    } //for each dimension
+    int nvol = 1; //convert all non-spatial volumes from source to destination
+    for (int vol = 4; vol < 8; vol++) {
+        if (h->dim[vol] > 1)
+            nvol = nvol * h->dim[vol];
+    }
+    reOrientImg(img, outDim, outInc, h->bitpix / 8,  nvol);
+    //now change the header....
+    vec3 outPix= {h->pixdim[abs(orientVec.v[0])],h->pixdim[abs(orientVec.v[1])],h->pixdim[abs(orientVec.v[2])]};
+    for (int i = 0; i < 3; i++) {
+        h->dim[i+1] = outDim.v[i];
+        h->pixdim[i+1] = outPix.v[i];
+    }
+    mat44 s = sFormMat(h);
+    mat33 mat; //computer transform
+    LOAD_MAT33(mat, s.m[0][0],s.m[0][1],s.m[0][2],
+                         s.m[1][0],s.m[1][1],s.m[1][2],
+                         s.m[2][0],s.m[2][1],s.m[2][2]);
+    mat = matMul33(  mat, orient);
+    s = setMat44Vec(mat, minMM); //add offset
+    mat2sForm(h,s);
+    h->qform_code = h->sform_code; //apply to the quaternion as well
+    float dumdx, dumdy, dumdz;
+    nifti_mat44_to_quatern( s , &h->quatern_b, &h->quatern_c, &h->quatern_d,&h->qoffset_x, &h->qoffset_y, &h->qoffset_z, &dumdx, &dumdy, &dumdz,&h->pixdim[0]) ;
+    return img;
+} //reOrient()
+
+float getDistance (vec3 v, vec3 min)
+//scalar distance between two 3D points - Pythagorean theorem
+{
+    return sqrt(pow((v.v[0]-min.v[0]),2)+pow((v.v[1]-min.v[1]),2)+pow((v.v[2]-min.v[2]),2)  );
+}
+
+vec3 xyz2mm (mat44 R, vec3 v)
+{
+    vec3 ret;
+    for (int i = 0; i < 3; i++) {
+        ret.v[i] = ( (R.m[i][0]*v.v[0])+(R.m[i][1]*v.v[1])+ (R.m[i][2]*v.v[2])+R.m[i][3] );
+    }
+    return ret;
+}
+
+vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec)
+//orthogonal rotations and reflections applied as 3x3 matrices will cause the origin to shift
+// a simple solution is to first compute the most left, posterior, inferior voxel in the source image
+// this voxel will be at location i,j,k = 0,0,0, so we can simply use this as the offset for the final 4x4 matrix...
+{
+    int i,j, minIndex;
+    vec3i flipVecs[8];
+    vec3 corner[8], min;
+    mat44 s = sFormMat(h);
+    for (int i = 0; i < 8; i++) {
+        if (i & 1) flipVecs[i].v[0] = -1; else flipVecs[i].v[0] = 1;
+        if (i & 2) flipVecs[i].v[1] = -1; else flipVecs[i].v[1] = 1;
+        if (i & 4) flipVecs[i].v[2] = -1; else flipVecs[i].v[2] = 1;
+        corner[i] = setVec3(0,0,0); //assume no reflections
+        if ((flipVecs[i].v[0]) < 1) corner[i].v[0] = h->dim[1]-1; //reflect X
+        if ((flipVecs[i].v[1]) < 1) corner[i].v[1] = h->dim[2]-1; //reflect Y
+        if ((flipVecs[i].v[2]) < 1) corner[i].v[2] = h->dim[3]-1; //reflect Z
+        corner[i] = xyz2mm(s,corner[i]);
+    }
+    //find extreme edge from ALL corners....
+    min = corner[0];
+    for (i = 1; i < 8; i++) {
+        for (j = 0; j < 3; j++) {
+            if (corner[i].v[j]< min.v[j]) min.v[j] = corner[i].v[j];
+        }
+    }
+    float dx; //observed distance from corner
+    float min_dx = getDistance (corner[0], min);
+    minIndex = 0; //index of corner closest to min
+    //see if any corner is closer to absmin than the first one...
+    for (i = 1; i < 8; i++) {
+        dx = getDistance (corner[i], min);
+        if (dx < min_dx) {
+            min_dx = dx;
+            minIndex = i;
+        }
+    }
+    min = corner[minIndex]; //this is the single corner closest to min from all
+    *flipVec= flipVecs[minIndex];
+    return min;
+}
+
+#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,
+           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]);
+}
+#endif
+
+unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
+    if ((h->dim[0] != 3) || (h->dim[1] < 1) || (h->dim[2] < 1) || (h->dim[3] < 1)) return img;
+    if ((h->sform_code == NIFTI_XFORM_UNKNOWN) && (h->qform_code != NIFTI_XFORM_UNKNOWN)) { //only q-form provided
+        mat44 q = nifti_quatern_to_mat44(h->quatern_b, h->quatern_c, h->quatern_d,
+                                         h->qoffset_x, h->qoffset_y, h->qoffset_z,
+                                         h->pixdim[1], h->pixdim[2], h->pixdim[3],h->pixdim[0]);
+        mat2sForm(h,q); //convert q-form to s-form
+        h->sform_code = h->qform_code;
+    }
+    if (h->sform_code == NIFTI_XFORM_UNKNOWN) {
+         #ifdef MY_DEBUG
+         printf("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");
+        #endif
+        return img;
+    }
+    vec3i  flipV;
+    vec3 minMM = minCornerFlip(h, &flipV);
+    mat33 orient = getBestOrient(s, flipV);
+    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");
+        #endif
+        return img;
+    }
+    bool is24 = false;
+    if (h->bitpix == 24 ) { //RGB stored as planar data. treat as 3 8-bit slices
+        return img;
+        is24 = true;
+        h->bitpix = 8;
+        h->dim[3] = h->dim[3] * 3;
+    }
+    img = reOrient(img, h,orientVec, orient, minMM);
+    if (is24 ) {
+        h->bitpix = 24;
+        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]);
+    reportMat44o("input",s);
+    s = sFormMat(h);
+    reportMat44o("output",s);
+    #endif
+    return img;
+}
+
+
+
diff --git a/wxWidgets/readme.txt b/wxWidgets/readme.txt
new file mode 100644
index 0000000..9d0fd20
--- /dev/null
+++ b/wxWidgets/readme.txt
@@ -0,0 +1,24 @@
+1.) wxWdigets makefiles are pretty complex and specific for your operating system. For simplicity, install wxWidgets and make the /samples/clipboard, then replace the original clipboard.cpp with our new program 
+
+
+2.) 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/' *
+
+For WX widgets it usually helps to "make" the original clipboard project on the computer and then edit the makefile:
+
+3.) 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
+	
+4.)  With wxWidgets we will capture std::cout comments, not printf, so we need to add "-DDmyUseCOut" to CXXFLAGS:
+CXXFLAGS = -DmyUseCOut -DWX_PRECOMP ....
+
+5.) For a full refresh
+rm clipboard
+rm *.o
+make
+
diff --git a/xcode/core b/xcode/core
new file mode 100644
index 0000000..cc65b7f
--- /dev/null
+++ b/xcode/core
@@ -0,0 +1,441 @@
+/*
+Copyright (c) 2013-2014, Cong Xu
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#pragma warning (disable : 4996)
+#else
+#include <dirent.h>
+#include <sys/stat.h>
+#endif
+
+
+/* types */
+
+#define _TINYDIR_PATH_MAX 4096
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+#define _TINYDIR_PATH_EXTRA 2
+#else
+#define _TINYDIR_PATH_EXTRA 0
+#endif
+#define _TINYDIR_FILENAME_MAX 256
+
+#ifdef _MSC_VER
+#define _TINYDIR_FUNC static __inline
+#else
+#define _TINYDIR_FUNC static __inline__
+#endif
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	char name[_TINYDIR_FILENAME_MAX];
+	int is_dir;
+	int is_reg;
+
+#ifdef _MSC_VER
+#else
+	struct stat _s;
+#endif
+} tinydir_file;
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	int has_next;
+	size_t n_files;
+
+	tinydir_file *_files;
+#ifdef _MSC_VER
+	HANDLE _h;
+	WIN32_FIND_DATA _f;
+#else
+	DIR *_d;
+	struct dirent *_e;
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path)
+{
+	if (dir == NULL || path == NULL || strlen(path) == 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	/* initialise dir */
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	dir->_d = NULL;
+#endif
+	tinydir_close(dir);
+
+	strcpy(dir->path, path);
+#ifdef _MSC_VER
+	strcat(dir->path, "\\*");
+	dir->_h = FindFirstFile(dir->path, &dir->_f);
+	dir->path[strlen(dir->path) - 2] = '\0';
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	dir->_d = opendir(path);
+	if (dir->_d == NULL)
+#endif
+	{
+		errno = ENOENT;
+		goto bail;
+	}
+	/* read first file */
+	dir->has_next = 1;
+#ifndef _MSC_VER
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+	{
+		dir->has_next = 0;
+	}
+#endif
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path)
+{
+	/* Count the number of files first, to pre-allocate the files array */
+	size_t n_files = 0;
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+	while (dir->has_next)
+	{
+		n_files++;
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+	}
+	tinydir_close(dir);
+
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	dir->n_files = 0;
+	dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files);
+	if (dir->_files == NULL)
+	{
+		errno = ENOMEM;
+		goto bail;
+	}
+	while (dir->has_next)
+	{
+		tinydir_file *p_file;
+		dir->n_files++;
+
+		p_file = &dir->_files[dir->n_files - 1];
+		if (tinydir_readfile(dir, p_file) == -1)
+		{
+			goto bail;
+		}
+
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+
+		/* Just in case the number of files has changed between the first and
+		second reads, terminate without writing into unallocated memory */
+		if (dir->n_files == n_files)
+		{
+			break;
+		}
+	}
+
+	qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		return;
+	}
+
+	memset(dir->path, 0, sizeof(dir->path));
+	dir->has_next = 0;
+	dir->n_files = 0;
+	if (dir->_files != NULL)
+	{
+		free(dir->_files);
+	}
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	if (dir->_h != INVALID_HANDLE_VALUE)
+	{
+		FindClose(dir->_h);
+	}
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	if (dir->_d)
+	{
+		closedir(dir->_d);
+	}
+	dir->_d = NULL;
+	dir->_e = NULL;
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (!dir->has_next)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+#ifdef _MSC_VER
+	if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+#endif
+	{
+		dir->has_next = 0;
+#ifdef _MSC_VER
+		if (GetLastError() != ERROR_SUCCESS &&
+			GetLastError() != ERROR_NO_MORE_FILES)
+		{
+			tinydir_close(dir);
+			errno = EIO;
+			return -1;
+		}
+#endif
+	}
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+#ifdef _MSC_VER
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	if (dir->_e == NULL)
+#endif
+	{
+		errno = ENOENT;
+		return -1;
+	}
+	if (strlen(dir->path) +
+		strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) + 1 + _TINYDIR_PATH_EXTRA >=
+		_TINYDIR_PATH_MAX)
+	{
+		/* the path for the file will be too long */
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+	if (strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) >= _TINYDIR_FILENAME_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	strcpy(file->path, dir->path);
+	strcat(file->path, "/");
+	strcpy(file->name,
+#ifdef _MSC_VER
+		dir->_f.cFileName
+#else
+		dir->_e->d_name
+#endif
+	);
+	strcat(file->path, file->name);
+#ifndef _MSC_VER
+	if (stat(file->path, &file->_s) == -1)
+	{
+		return -1;
+	}
+#endif
+	file->is_dir =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+		S_ISDIR(file->_s.st_mode);
+#endif
+	file->is_reg =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+		(
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+		S_ISREG(file->_s.st_mode);
+#endif
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+	char path[_TINYDIR_PATH_MAX];
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files || !dir->_files[i].is_dir)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	strcpy(path, dir->_files[i].path);
+	tinydir_close(dir);
+	if (tinydir_open_sorted(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+	const tinydir_file *fa = (const tinydir_file *)a;
+	const tinydir_file *fb = (const tinydir_file *)b;
+	if (fa->is_dir != fb->is_dir)
+	{
+		return -(fa->is_dir - fb->is_dir);
+	}
+	return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#endif
diff --git a/xcode/dcm2.xcodeproj/project.pbxproj b/xcode/dcm2.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b73815a
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/project.pbxproj
@@ -0,0 +1,390 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		4F1D666719F422D6004BEC23 /* nifti1_io_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */; };
+		4F1D666919F422D6004BEC23 /* nii_dicom_batch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */; };
+		4F1D666B19F422D6004BEC23 /* nii_dicom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666519F422D6004BEC23 /* nii_dicom.cpp */; };
+		4F1D666D19F422D6004BEC23 /* nii_ortho.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F1D666619F422D6004BEC23 /* nii_ortho.cpp */; };
+		4F20AC5E191D67F600EA3B33 /* libz.1.2.5.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */; };
+		4F7688E21B173D7D00E959F3 /* miniz.c in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E11B173D7D00E959F3 /* miniz.c */; };
+		4F7688E51B173EBB00E959F3 /* jpg_0XC3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */; };
+		4F7688E81B17402B00E959F3 /* ujpeg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4F7688E61B17402B00E959F3 /* ujpeg.cpp */; };
+		4F9EB64718F333DF00F6A696 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F9EB64618F333DF00F6A696 /* Cocoa.framework */; };
+		4F9EB65118F333DF00F6A696 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */; };
+		4F9EB65318F333DF00F6A696 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9EB65218F333DF00F6A696 /* main.m */; };
+		4F9EB65718F333DF00F6A696 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB65518F333DF00F6A696 /* Credits.rtf */; };
+		4F9EB65D18F333DF00F6A696 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4F9EB65B18F333DF00F6A696 /* MainMenu.xib */; };
+		4FE4DAD318FF778100EDF667 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9EB65918F333DF00F6A696 /* AppDelegate.m */; };
+		4FE5DAC618F338FA003B4508 /* dcm2niigui.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */; };
+		4FE5DACC18F33AEE003B4508 /* myWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5DACB18F33AEE003B4508 /* myWindow.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		4F1D665D19F422D6004BEC23 /* nifti1_io_core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nifti1_io_core.h; sourceTree = "<group>"; };
+		4F1D665E19F422D6004BEC23 /* nifti1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nifti1.h; sourceTree = "<group>"; };
+		4F1D665F19F422D6004BEC23 /* nii_dicom_batch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_dicom_batch.h; sourceTree = "<group>"; };
+		4F1D666019F422D6004BEC23 /* nii_dicom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_dicom.h; sourceTree = "<group>"; };
+		4F1D666119F422D6004BEC23 /* nii_ortho.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nii_ortho.h; sourceTree = "<group>"; };
+		4F1D666219F422D6004BEC23 /* tinydir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinydir.h; sourceTree = "<group>"; };
+		4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nifti1_io_core.cpp; sourceTree = "<group>"; };
+		4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_dicom_batch.cpp; sourceTree = "<group>"; };
+		4F1D666519F422D6004BEC23 /* nii_dicom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_dicom.cpp; sourceTree = "<group>"; };
+		4F1D666619F422D6004BEC23 /* nii_ortho.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nii_ortho.cpp; sourceTree = "<group>"; };
+		4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.2.5.dylib; path = ../../../../../../usr/lib/libz.1.2.5.dylib; sourceTree = "<group>"; };
+		4F7688E11B173D7D00E959F3 /* miniz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = miniz.c; sourceTree = "<group>"; };
+		4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jpg_0XC3.cpp; sourceTree = "<group>"; };
+		4F7688E41B173EBB00E959F3 /* jpg_0XC3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jpg_0XC3.h; sourceTree = "<group>"; };
+		4F7688E61B17402B00E959F3 /* ujpeg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ujpeg.cpp; sourceTree = "<group>"; };
+		4F7688E71B17402B00E959F3 /* ujpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ujpeg.h; sourceTree = "<group>"; };
+		4F9EB64318F333DF00F6A696 /* dcm2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = dcm2.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		4F9EB64618F333DF00F6A696 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+		4F9EB64918F333DF00F6A696 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+		4F9EB64A18F333DF00F6A696 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+		4F9EB64B18F333DF00F6A696 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		4F9EB64E18F333DF00F6A696 /* dcm2-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "dcm2-Info.plist"; sourceTree = "<group>"; };
+		4F9EB65018F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		4F9EB65218F333DF00F6A696 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		4F9EB65418F333DF00F6A696 /* dcm2-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "dcm2-Prefix.pch"; sourceTree = "<group>"; };
+		4F9EB65618F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
+		4F9EB65818F333DF00F6A696 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		4F9EB65918F333DF00F6A696 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		4F9EB65C18F333DF00F6A696 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
+		4F9EB66418F333DF00F6A696 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+		4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = dcm2niigui.icns; sourceTree = "<group>"; };
+		4FE5DACA18F33AEE003B4508 /* myWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = myWindow.h; sourceTree = "<group>"; };
+		4FE5DACB18F33AEE003B4508 /* myWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = myWindow.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		4F9EB64018F333DF00F6A696 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				4F20AC5E191D67F600EA3B33 /* libz.1.2.5.dylib in Frameworks */,
+				4F9EB64718F333DF00F6A696 /* Cocoa.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		4F9EB63A18F333DF00F6A696 = {
+			isa = PBXGroup;
+			children = (
+				4F20AC5D191D67F600EA3B33 /* libz.1.2.5.dylib */,
+				4FE5DAC518F338FA003B4508 /* dcm2niigui.icns */,
+				4F9EB64C18F333DF00F6A696 /* dcm2 */,
+				4F9EB64518F333DF00F6A696 /* Frameworks */,
+				4F9EB64418F333DF00F6A696 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		4F9EB64418F333DF00F6A696 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				4F9EB64318F333DF00F6A696 /* dcm2.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		4F9EB64518F333DF00F6A696 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				4F9EB64618F333DF00F6A696 /* Cocoa.framework */,
+				4F9EB66418F333DF00F6A696 /* SenTestingKit.framework */,
+				4F9EB64818F333DF00F6A696 /* Other Frameworks */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		4F9EB64818F333DF00F6A696 /* Other Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				4F9EB64918F333DF00F6A696 /* AppKit.framework */,
+				4F9EB64A18F333DF00F6A696 /* CoreData.framework */,
+				4F9EB64B18F333DF00F6A696 /* Foundation.framework */,
+			);
+			name = "Other Frameworks";
+			sourceTree = "<group>";
+		};
+		4F9EB64C18F333DF00F6A696 /* dcm2 */ = {
+			isa = PBXGroup;
+			children = (
+				4FADC891190AD70F00010758 /* core */,
+				4F9EB65818F333DF00F6A696 /* AppDelegate.h */,
+				4F9EB65918F333DF00F6A696 /* AppDelegate.m */,
+				4FE5DACA18F33AEE003B4508 /* myWindow.h */,
+				4FE5DACB18F33AEE003B4508 /* myWindow.m */,
+				4F9EB65B18F333DF00F6A696 /* MainMenu.xib */,
+				4F9EB64D18F333DF00F6A696 /* Supporting Files */,
+			);
+			path = dcm2;
+			sourceTree = "<group>";
+		};
+		4F9EB64D18F333DF00F6A696 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				4F9EB64E18F333DF00F6A696 /* dcm2-Info.plist */,
+				4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */,
+				4F9EB65218F333DF00F6A696 /* main.m */,
+				4F9EB65418F333DF00F6A696 /* dcm2-Prefix.pch */,
+				4F9EB65518F333DF00F6A696 /* Credits.rtf */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		4FADC891190AD70F00010758 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				4F7688E61B17402B00E959F3 /* ujpeg.cpp */,
+				4F7688E71B17402B00E959F3 /* ujpeg.h */,
+				4F1D665D19F422D6004BEC23 /* nifti1_io_core.h */,
+				4F1D665E19F422D6004BEC23 /* nifti1.h */,
+				4F7688E31B173EBB00E959F3 /* jpg_0XC3.cpp */,
+				4F7688E41B173EBB00E959F3 /* jpg_0XC3.h */,
+				4F1D666019F422D6004BEC23 /* nii_dicom.h */,
+				4F1D666519F422D6004BEC23 /* nii_dicom.cpp */,
+				4F1D665F19F422D6004BEC23 /* nii_dicom_batch.h */,
+				4F1D666419F422D6004BEC23 /* nii_dicom_batch.cpp */,
+				4F1D666119F422D6004BEC23 /* nii_ortho.h */,
+				4F1D666219F422D6004BEC23 /* tinydir.h */,
+				4F1D666319F422D6004BEC23 /* nifti1_io_core.cpp */,
+				4F1D666619F422D6004BEC23 /* nii_ortho.cpp */,
+				4F7688E11B173D7D00E959F3 /* miniz.c */,
+			);
+			path = core;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		4F9EB64218F333DF00F6A696 /* dcm2 */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 4F9EB67418F333DF00F6A696 /* Build configuration list for PBXNativeTarget "dcm2" */;
+			buildPhases = (
+				4F9EB63F18F333DF00F6A696 /* Sources */,
+				4F9EB64018F333DF00F6A696 /* Frameworks */,
+				4F9EB64118F333DF00F6A696 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = dcm2;
+			productName = dcm2;
+			productReference = 4F9EB64318F333DF00F6A696 /* dcm2.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		4F9EB63B18F333DF00F6A696 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastTestingUpgradeCheck = 0640;
+				LastUpgradeCheck = 0610;
+				ORGANIZATIONNAME = "Chris Rorden";
+			};
+			buildConfigurationList = 4F9EB63E18F333DF00F6A696 /* Build configuration list for PBXProject "dcm2" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = 4F9EB63A18F333DF00F6A696;
+			productRefGroup = 4F9EB64418F333DF00F6A696 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				4F9EB64218F333DF00F6A696 /* dcm2 */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		4F9EB64118F333DF00F6A696 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				4F9EB65118F333DF00F6A696 /* InfoPlist.strings in Resources */,
+				4F9EB65718F333DF00F6A696 /* Credits.rtf in Resources */,
+				4F9EB65D18F333DF00F6A696 /* MainMenu.xib in Resources */,
+				4FE5DAC618F338FA003B4508 /* dcm2niigui.icns in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		4F9EB63F18F333DF00F6A696 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				4F7688E51B173EBB00E959F3 /* jpg_0XC3.cpp in Sources */,
+				4F9EB65318F333DF00F6A696 /* main.m in Sources */,
+				4FE5DACC18F33AEE003B4508 /* myWindow.m in Sources */,
+				4FE4DAD318FF778100EDF667 /* AppDelegate.m in Sources */,
+				4F1D666719F422D6004BEC23 /* nifti1_io_core.cpp in Sources */,
+				4F7688E81B17402B00E959F3 /* ujpeg.cpp in Sources */,
+				4F1D666919F422D6004BEC23 /* nii_dicom_batch.cpp in Sources */,
+				4F1D666B19F422D6004BEC23 /* nii_dicom.cpp in Sources */,
+				4F1D666D19F422D6004BEC23 /* nii_ortho.cpp in Sources */,
+				4F7688E21B173D7D00E959F3 /* miniz.c in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		4F9EB64F18F333DF00F6A696 /* InfoPlist.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				4F9EB65018F333DF00F6A696 /* en */,
+			);
+			name = InfoPlist.strings;
+			sourceTree = "<group>";
+		};
+		4F9EB65518F333DF00F6A696 /* Credits.rtf */ = {
+			isa = PBXVariantGroup;
+			children = (
+				4F9EB65618F333DF00F6A696 /* en */,
+			);
+			name = Credits.rtf;
+			sourceTree = "<group>";
+		};
+		4F9EB65B18F333DF00F6A696 /* MainMenu.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				4F9EB65C18F333DF00F6A696 /* en */,
+			);
+			name = MainMenu.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		4F9EB67218F333DF00F6A696 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libstdc++";
+				CLANG_ENABLE_OBJC_ARC = NO;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					myDisableMiniZ,
+					myDisableOpenJPEG,
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = "";
+			};
+			name = Debug;
+		};
+		4F9EB67318F333DF00F6A696 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libstdc++";
+				CLANG_ENABLE_OBJC_ARC = NO;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = YES;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					myDisableMiniZ,
+					myDisableOpenJPEG,
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				SDKROOT = "";
+			};
+			name = Release;
+		};
+		4F9EB67518F333DF00F6A696 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = NO;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEAD_CODE_STRIPPING = YES;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "dcm2/dcm2-Prefix.pch";
+				INFOPLIST_FILE = "dcm2/dcm2-Info.plist";
+				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Debug;
+		};
+		4F9EB67618F333DF00F6A696 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = NO;
+				COMBINE_HIDPI_IMAGES = YES;
+				DEAD_CODE_STRIPPING = YES;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "dcm2/dcm2-Prefix.pch";
+				INFOPLIST_FILE = "dcm2/dcm2-Info.plist";
+				MACOSX_DEPLOYMENT_TARGET = 10.6;
+				ONLY_ACTIVE_ARCH = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				WRAPPER_EXTENSION = app;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		4F9EB63E18F333DF00F6A696 /* Build configuration list for PBXProject "dcm2" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				4F9EB67218F333DF00F6A696 /* Debug */,
+				4F9EB67318F333DF00F6A696 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		4F9EB67418F333DF00F6A696 /* Build configuration list for PBXNativeTarget "dcm2" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				4F9EB67518F333DF00F6A696 /* Debug */,
+				4F9EB67618F333DF00F6A696 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 4F9EB63B18F333DF00F6A696 /* Project object */;
+}
diff --git a/xcode/dcm2.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/xcode/dcm2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..29769e3
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:dcm2.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/xcode/dcm2.xcodeproj/project.xcworkspace/xcshareddata/dcm2.xccheckout b/xcode/dcm2.xcodeproj/project.xcworkspace/xcshareddata/dcm2.xccheckout
new file mode 100644
index 0000000..2ef2bf6
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/project.xcworkspace/xcshareddata/dcm2.xccheckout
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDESourceControlProjectFavoriteDictionaryKey</key>
+	<false/>
+	<key>IDESourceControlProjectIdentifier</key>
+	<string>C798AF42-BE64-44EE-87C0-C50C9566D382</string>
+	<key>IDESourceControlProjectName</key>
+	<string>dcm2</string>
+	<key>IDESourceControlProjectOriginsDictionary</key>
+	<dict>
+		<key>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</key>
+		<string>https://github.com/neurolabusc/dcm2niix.git</string>
+	</dict>
+	<key>IDESourceControlProjectPath</key>
+	<string>xcode/dcm2.xcodeproj</string>
+	<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+	<dict>
+		<key>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</key>
+		<string>../../..</string>
+	</dict>
+	<key>IDESourceControlProjectURL</key>
+	<string>https://github.com/neurolabusc/dcm2niix.git</string>
+	<key>IDESourceControlProjectVersion</key>
+	<integer>111</integer>
+	<key>IDESourceControlProjectWCCIdentifier</key>
+	<string>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</string>
+	<key>IDESourceControlProjectWCConfigurations</key>
+	<array>
+		<dict>
+			<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+			<string>public.vcs.git</string>
+			<key>IDESourceControlWCCIdentifierKey</key>
+			<string>D94D2C5489FA4C6F294552E6DEAD453AC2154E05</string>
+			<key>IDESourceControlWCCName</key>
+			<string>dcm2niix</string>
+		</dict>
+	</array>
+</dict>
+</plist>
diff --git a/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/administrator.xcuserdatad/UserInterfaceState.xcuserstate b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/administrator.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..3168066
Binary files /dev/null and b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/administrator.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/UserInterfaceState.xcuserstate b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..40e9a69
Binary files /dev/null and b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/WorkspaceSettings.xcsettings b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..bfffcfe
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/project.xcworkspace/xcuserdata/rorden.xcuserdatad/WorkspaceSettings.xcsettings
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
+	<true/>
+	<key>SnapshotAutomaticallyBeforeSignificantChanges</key>
+	<false/>
+</dict>
+</plist>
diff --git a/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/dcm2.xcscheme b/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/dcm2.xcscheme
new file mode 100644
index 0000000..dd67e19
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/dcm2.xcscheme
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+               BuildableName = "dcm2.app"
+               BlueprintName = "dcm2"
+               ReferencedContainer = "container:dcm2.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4F9EB66218F333DF00F6A696"
+               BuildableName = "dcm2Tests.octest"
+               BlueprintName = "dcm2Tests"
+               ReferencedContainer = "container:dcm2.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/xcschememanagement.plist b/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..8ef8e1f
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/xcuserdata/administrator.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>dcm2.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>4F9EB64218F333DF00F6A696</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+		<key>4F9EB66218F333DF00F6A696</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>
diff --git a/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
new file mode 100644
index 0000000..05301bc
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+   type = "1"
+   version = "1.0">
+</Bucket>
diff --git a/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/dcm2.xcscheme b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/dcm2.xcscheme
new file mode 100644
index 0000000..c8d2ae7
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/dcm2.xcscheme
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0610"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+               BuildableName = "dcm2.app"
+               BlueprintName = "dcm2"
+               ReferencedContainer = "container:dcm2.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "4F9EB66218F333DF00F6A696"
+               BuildableName = "dcm2Tests.octest"
+               BlueprintName = "dcm2Tests"
+               ReferencedContainer = "container:dcm2.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "4F9EB64218F333DF00F6A696"
+            BuildableName = "dcm2.app"
+            BlueprintName = "dcm2"
+            ReferencedContainer = "container:dcm2.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/xcschememanagement.plist b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..8ef8e1f
--- /dev/null
+++ b/xcode/dcm2.xcodeproj/xcuserdata/rorden.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>dcm2.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>4F9EB64218F333DF00F6A696</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+		<key>4F9EB66218F333DF00F6A696</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>
diff --git a/xcode/dcm2/AppDelegate.h b/xcode/dcm2/AppDelegate.h
new file mode 100644
index 0000000..2058796
--- /dev/null
+++ b/xcode/dcm2/AppDelegate.h
@@ -0,0 +1,41 @@
+//
+//  AppDelegate.h
+//  dcm2
+//
+//  Created by Chris Rorden on 4/7/14.
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//
+
+//for NSTextView, turn off non-contiguous layout for background refresh
+
+#import <Cocoa/Cocoa.h>
+#include "./core/nii_dicom_batch.h"
+
+ at interface AppDelegate : NSObject <NSApplicationDelegate> {
+    struct TDCMopts opts;
+    dispatch_source_t source;
+#if __has_feature(objc_arc_weak)
+    //automatic
+#else
+    NSWindow *_window;
+    NSTextView *_theTextView;
+    NSButton *_compressCheck;
+    NSTextField *_outputFilenameEdit;
+    NSButton *_folderButton;
+#endif
+}
+ at property (assign) IBOutlet NSButton *compressCheck;
+ at property (assign) IBOutlet NSTextField *outputFilenameEdit;
+ at property (assign) IBOutlet NSButton *folderButton;
+ at property (assign) IBOutlet NSWindow *window;
+ at property (assign) IBOutlet NSTextView *theTextView;
+- (void) processFile: (NSString*) fname;
+- (IBAction)outputFolderClick:(id)sender;
+- (IBAction)par2niiClick:(id)sender;
+- (IBAction)dicom2niiClick:(id)sender;
+//- (IBAction)compressCheckClick:(id)sender;
+- (IBAction)compressCheckClick:(id)sender;
+
+
+
+ at end
diff --git a/xcode/dcm2/AppDelegate.m b/xcode/dcm2/AppDelegate.m
new file mode 100644
index 0000000..b1d39ce
--- /dev/null
+++ b/xcode/dcm2/AppDelegate.m
@@ -0,0 +1,201 @@
+//
+//  AppDelegate.m
+//  dcm2
+//
+//  Created by Chris Rorden on 4/7/14.
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//
+
+#import "AppDelegate.h"
+#include "./core/nii_dicom_batch.h"
+
+ at implementation AppDelegate
+#if __has_feature(objc_arc_weak)
+//automatic
+#else
+ at synthesize window = _window;
+ at synthesize theTextView = _theTextView;
+ at synthesize compressCheck = _compressCheck;
+ at synthesize outputFilenameEdit = _outputFilenameEdit;
+ at synthesize folderButton = _folderButton;
+#endif
+
+- (void) processFile: (NSString*) fname
+{
+    //convert folder (DICOM) or file (PAR/REC) to NIFTI format
+    [_theTextView setString: @""];//clear display
+    struct TDCMopts optsTemp;
+    optsTemp = opts; //conversion may change values like the outdir (if not specified)
+    strcpy(optsTemp.indir, [fname cStringUsingEncoding:1]);
+    optsTemp.isVerbose = true;
+    clock_t start = clock();
+    nii_loadDir (&(optsTemp));
+    printf("required %fms\n", ((double)(clock()-start))/1000);
+    fflush(stdout); //GUI buffers printf, display all results
+}
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { //convert images
+    [self processFile: filename];
+    return true;
+}
+
+-(void) showExampleFilename {
+    char niiFilename[1024];
+    nii_createDummyFilename(niiFilename, opts);
+    [self.theTextView setString: [NSString stringWithFormat:@"%s\nVersion %s\n", niiFilename,kDCMvers ]];//clear display
+    //printf("Version %s\n",kDCMvers);
+    //[self.theTextView setString: [NSString stringWithFormat:@"Output images will have names like %s", niiFilename ]];//clear display
+    [self.theTextView setNeedsDisplay:YES];
+}
+
+- (void)controlTextDidChange:(NSNotification *)notification {
+    //user has changed text field, 1: get desired file name mask
+    NSTextField *textField = [notification object];
+    //next: display example of what the provided filename mask will generate
+    strcpy(opts.filename, [[textField stringValue] cStringUsingEncoding:1]);
+    [self showExampleFilename];
+}
+
+-(void)showPrefs {
+    _compressCheck.state = opts.isGz;
+    //NSString *title = [NSString stringWithCString:opts.filename encoding:NSASCIIStringEncoding];
+    //[_outputFilenameEdit setStringValue:title];
+    [_outputFilenameEdit setStringValue:[NSString stringWithCString:opts.filename encoding:NSASCIIStringEncoding]];
+    NSString *outdir = [NSString stringWithCString:opts.outdir encoding:NSASCIIStringEncoding];
+    if ([outdir length] < 1)
+        [_folderButton setTitle:@"input folder"];
+    else if ([outdir length] > 40)
+        [_folderButton setTitle:[NSString stringWithFormat:@"%@%@", @"...",[outdir substringFromIndex:[outdir length]-36]]];
+    else
+        [_folderButton setTitle:outdir];
+    [self showExampleFilename];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+    //re-direct printf statements to appear in the text view component
+    //Here we use Grand Central Dispatch, alternatively we could use NSNotification as described here:
+    //   http://stackoverflow.com/questions/2406204/what-is-the-best-way-to-redirect-stdout-to-nstextview-in-cocoa
+    NSPipe* pipe = [NSPipe pipe];
+    NSFileHandle* pipeReadHandle = [pipe fileHandleForReading];
+    dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout));
+    source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [pipeReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
+    dispatch_source_set_event_handler(source, ^{
+        void* data = malloc(4096);
+        ssize_t readResult = 0;
+        do
+        {
+            errno = 0;
+            readResult = read([pipeReadHandle fileDescriptor], data, 4096);
+        } while (readResult == -1 && errno == EINTR);
+        if (readResult > 0) {
+            //AppKit UI should only be updated from the main thread
+            NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
+            dispatch_async(dispatch_get_main_queue(),^{
+                [[[_theTextView textStorage] mutableString] appendString:stdOutString];
+                [_theTextView setNeedsDisplay:YES];
+            });
+            [stdOutString release];
+        }
+        else{free(data);}
+    });
+    dispatch_resume(source);
+    //read and display preferences
+    const char *appPath = [[[NSBundle mainBundle] bundlePath] UTF8String];
+   readIniFile (&opts, &appPath);
+    [self showPrefs];
+    fflush(stdout); //GUI buffers printf, display all results
+    //finally, remove any bizarre options that XCode appends to Edit menu
+    NSMenu* edit = [[[[NSApplication sharedApplication] mainMenu] itemWithTitle: @"Edit"] submenu];
+    if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"orderFrontCharacterPalette:"))
+        [edit removeItemAtIndex: [edit numberOfItems] - 1];
+    if ([[edit itemAtIndex: [edit numberOfItems] - 1] action] == NSSelectorFromString(@"startDictation:"))
+        [edit removeItemAtIndex: [edit numberOfItems] - 1];
+    if ([[edit itemAtIndex: [edit numberOfItems] - 1] isSeparatorItem])
+        [edit removeItemAtIndex: [edit numberOfItems] - 1];
+    #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_6
+        //NSLog(@"yyyy");
+    #endif
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
+    return YES;
+}
+
+- (IBAction)compressCheckClick:(id)sender {
+    opts.isGz = _compressCheck.state;
+    [self showExampleFilename];
+}
+
+- (IBAction)outputFolderClick:(id)sender {
+    //http://stackoverflow.com/questions/5621513/cocoa-select-choose-file-panel
+    NSOpenPanel* openDlg = [NSOpenPanel openPanel];
+    [openDlg setTitle: @"Select output folder (cancel to use input folder)"];
+    [openDlg setCanChooseFiles:NO];
+    [openDlg setCanChooseDirectories:YES];
+    [openDlg setPrompt:@"Select"];
+    NSInteger isOKButton = [openDlg runModal];
+    NSArray* files = [openDlg URLs];
+    NSString* outdir = @"";
+    if ((isOKButton == NSOKButton)  && ([files count] > 0))
+        outdir = [[files objectAtIndex:0] path];
+    strcpy(opts.outdir, [outdir cStringUsingEncoding:1]);
+    [self showPrefs];
+}
+
+- (IBAction)dicom2niiClick:(id)sender {
+    NSOpenPanel* openDlg = [NSOpenPanel openPanel];
+    [openDlg setTitle: @"Select folder that contains DICOM images"];
+    [openDlg setCanChooseFiles:NO];
+    [openDlg setCanChooseDirectories:YES];
+    [openDlg setPrompt:@"Select"];
+    if ([openDlg runModal] != NSOKButton ) return;
+    NSArray* files = [openDlg URLs];
+    if ([files count] < 1) return;
+    [self processFile: [[files objectAtIndex:0] path] ];
+}
+
+- (IBAction)par2niiClick:(id)sender {
+    #if __has_feature(objc_arc_weak)
+    //NSOpenPanel *panel;
+    #else
+    //NSOpenPanel *panel = [[NSOpenPanel alloc] init];
+    #endif
+    NSOpenPanel* panel = [NSOpenPanel openPanel];
+    NSArray* fileTypes = [[NSArray alloc] initWithObjects:@"par", @"PAR", nil];
+    [panel setTitle: @"Select Philips PAR images"];
+    panel = [NSOpenPanel openPanel];
+    [panel setFloatingPanel:YES];
+    [panel setCanChooseDirectories:NO];
+    [panel setCanChooseFiles:YES];
+    [panel setAllowsMultipleSelection:YES];
+    [panel setAllowedFileTypes:fileTypes];
+    if ([panel runModal] != NSOKButton) {
+        [fileTypes release];
+        return;
+    }
+    [fileTypes release];
+    NSArray* files = [panel URLs];
+    NSUInteger n = [files count];
+    if (n < 1) return;
+    for (int i = 0; i < n; i++)
+        [self processFile: [[files objectAtIndex:i] path] ];
+
+}
+
+-(void) dealloc {
+#if __has_feature(objc_arc_weak)
+    //automatic
+#else
+    [super dealloc];
+#endif
+    
+}
+
+-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
+{
+    saveIniFile (opts); //save preferences
+    return NSTerminateNow;
+}
+
+
+ at end
diff --git a/xcode/dcm2/dcm2-Info.plist b/xcode/dcm2/dcm2-Info.plist
new file mode 100644
index 0000000..594c2dc
--- /dev/null
+++ b/xcode/dcm2/dcm2-Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleDocumentTypes</key>
+	<array>
+		<dict>
+			<key>CFBundleTypeExtensions</key>
+			<array>
+				<string>*</string>
+			</array>
+			<key>CFBundleTypeName</key>
+			<string>NSFilenamesPboardType</string>
+			<key>CFBundleTypeRole</key>
+			<string>None</string>
+		</dict>
+	</array>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string>dcm2niigui</string>
+	<key>CFBundleIdentifier</key>
+	<string>www.mricro.com.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
+	<key>NSHumanReadableCopyright</key>
+	<string>Copyright © 2014 Chris Rorden. All rights reserved.</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/xcode/dcm2/dcm2-Prefix.pch b/xcode/dcm2/dcm2-Prefix.pch
new file mode 100644
index 0000000..6f18219
--- /dev/null
+++ b/xcode/dcm2/dcm2-Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'dcm2' target in the 'dcm2' project
+//
+
+#ifdef __OBJC__
+    #import <Cocoa/Cocoa.h>
+#endif
diff --git a/xcode/dcm2/en.lproj/Credits.rtf b/xcode/dcm2/en.lproj/Credits.rtf
new file mode 100644
index 0000000..86bc97d
--- /dev/null
+++ b/xcode/dcm2/en.lproj/Credits.rtf
@@ -0,0 +1,23 @@
+{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\paperw9840\paperh8400
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
+
+\f0\b\fs24 \cf0 About:
+\b0 \
+	This software is designed to convert medical images stored in complicated formats (DICOM or Philips proprietary PAR/REC) to the simple NIfTI format used by popular scientific tools. For DICOM images, simply drop the folder containing the files onto this software. For Philips PAR/REC images, drag and drop the file you wish to convert. This software attempts to preserve spatial orientation and position. However, you should be careful to validate and inspect images. In particular, verify t [...]
+\
+
+\b License:
+\b0 \
+Copyright (c) 2014, Chris Rorden\
+All rights reserved.\
+\
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: \
+\
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. \
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. \
+\
+This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profi [...]
+\
+For more information, see www.mricro.com}
diff --git a/xcode/dcm2/en.lproj/InfoPlist.strings b/xcode/dcm2/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/xcode/dcm2/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/xcode/dcm2/en.lproj/MainMenu.xib b/xcode/dcm2/en.lproj/MainMenu.xib
new file mode 100644
index 0000000..31e238b
--- /dev/null
+++ b/xcode/dcm2/en.lproj/MainMenu.xib
@@ -0,0 +1,1242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">13E28</string>
+		<string key="IBDocument.InterfaceBuilderVersion">3084</string>
+		<string key="IBDocument.AppKitVersion">1265.21</string>
+		<string key="IBDocument.HIToolboxVersion">698.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">3084</string>
+		</object>
+		<array key="IBDocument.IntegratedClassDependencies">
+			<string>NSButton</string>
+			<string>NSButtonCell</string>
+			<string>NSCustomObject</string>
+			<string>NSMenu</string>
+			<string>NSMenuItem</string>
+			<string>NSScrollView</string>
+			<string>NSScroller</string>
+			<string>NSTextField</string>
+			<string>NSTextFieldCell</string>
+			<string>NSTextView</string>
+			<string>NSView</string>
+			<string>NSWindowTemplate</string>
+		</array>
+		<array key="IBDocument.PluginDependencies">
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</array>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">AMainMenu</string>
+				<array class="NSMutableArray" key="NSMenuItems">
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">dcm2</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="35465992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="502551668">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<reference key="NSTarget" ref="110575045"/>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">dcm2</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="238522557">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">About dcm2</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="304266470">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="609285721">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="481834944">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1046388886">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<reference key="NSTarget" ref="752062318"/>
+									<object class="NSMenu" key="NSSubmenu" id="752062318">
+										<string key="NSTitle">Services</string>
+										<array class="NSMutableArray" key="NSMenuItems"/>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646227648">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="755159360">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide dcm2</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="342932134">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="908899353">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1056857174">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit dcm2</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="379814623">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<reference key="NSTarget" ref="720053764"/>
+						<object class="NSMenu" key="NSSubmenu" id="720053764">
+							<string key="NSTitle">File</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="722745758">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">DICOM to NIfTI...</string>
+									<string key="NSKeyEquiv">d</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="482520206">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">PAR/REC to NIfTI...</string>
+									<string key="NSKeyEquiv">d</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="952259628">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<reference key="NSTarget" ref="789758025"/>
+						<object class="NSMenu" key="NSSubmenu" id="789758025">
+							<string key="NSTitle">Edit</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="860595796">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="583158037">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+						</object>
+					</object>
+				</array>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSWindowTemplate" id="972006081">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{10, 10}, {640, 300}}</string>
+				<int key="NSWTFlags">1954021376</int>
+				<string key="NSWindowTitle">dcm2</string>
+				<string key="NSWindowClass">myWindow</string>
+				<nil key="NSViewClass"/>
+				<nil key="NSUserInterfaceItemIdentifier"/>
+				<string key="NSWindowContentMinSize">{640, 120}</string>
+				<object class="NSView" key="NSWindowView" id="439893737">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<array class="NSMutableArray" key="NSSubviews">
+						<object class="NSScrollView" id="248496456">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">274</int>
+							<array class="NSMutableArray" key="NSSubviews">
+								<object class="NSClipView" id="455232561">
+									<reference key="NSNextResponder" ref="248496456"/>
+									<int key="NSvFlags">2322</int>
+									<array class="NSMutableArray" key="NSSubviews">
+										<object class="NSTextView" id="746145959">
+											<reference key="NSNextResponder" ref="455232561"/>
+											<int key="NSvFlags">2322</int>
+											<string key="NSFrameSize">{625, 266}</string>
+											<reference key="NSSuperview" ref="455232561"/>
+											<reference key="NSWindow"/>
+											<reference key="NSNextKeyView" ref="992720392"/>
+											<string key="NSReuseIdentifierKey">_NS:13</string>
+											<object class="NSTextContainer" key="NSTextContainer" id="33714781">
+												<object class="NSLayoutManager" key="NSLayoutManager">
+													<object class="NSTextStorage" key="NSTextStorage">
+														<object class="NSMutableString" key="NSString">
+															<characters key="NS.bytes"/>
+														</object>
+														<nil key="NSDelegate"/>
+													</object>
+													<array class="NSMutableArray" key="NSTextContainers">
+														<reference ref="33714781"/>
+													</array>
+													<int key="NSLMFlags">38</int>
+													<nil key="NSDelegate"/>
+												</object>
+												<reference key="NSTextView" ref="746145959"/>
+												<double key="NSWidth">625</double>
+												<int key="NSTCFlags">1</int>
+											</object>
+											<object class="NSTextViewSharedData" key="NSSharedData">
+												<int key="NSFlags">67121121</int>
+												<int key="NSTextCheckingTypes">0</int>
+												<nil key="NSMarkedAttributes"/>
+												<object class="NSColor" key="NSBackgroundColor" id="640036751">
+													<int key="NSColorSpace">3</int>
+													<bytes key="NSWhite">MQA</bytes>
+												</object>
+												<dictionary key="NSSelectedAttributes">
+													<object class="NSColor" key="NSBackgroundColor">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">selectedTextBackgroundColor</string>
+														<object class="NSColor" key="NSColor" id="426337731">
+															<int key="NSColorSpace">3</int>
+															<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
+														</object>
+													</object>
+													<object class="NSColor" key="NSColor">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">selectedTextColor</string>
+														<object class="NSColor" key="NSColor" id="971210461">
+															<int key="NSColorSpace">3</int>
+															<bytes key="NSWhite">MAA</bytes>
+														</object>
+													</object>
+												</dictionary>
+												<reference key="NSInsertionColor" ref="971210461"/>
+												<dictionary key="NSLinkAttributes">
+													<object class="NSColor" key="NSColor">
+														<int key="NSColorSpace">1</int>
+														<bytes key="NSRGB">MCAwIDEAA</bytes>
+													</object>
+													<object class="NSCursor" key="NSCursor">
+														<string key="NSHotSpot">{8, -8}</string>
+														<int key="NSCursorType">13</int>
+													</object>
+													<integer value="1" key="NSUnderline"/>
+												</dictionary>
+												<nil key="NSDefaultParagraphStyle"/>
+												<nil key="NSTextFinder"/>
+												<int key="NSPreferredTextFinderStyle">1</int>
+											</object>
+											<int key="NSTVFlags">6</int>
+											<string key="NSMaxSize">{755, 10000000}</string>
+											<nil key="NSDelegate"/>
+										</object>
+									</array>
+									<string key="NSFrame">{{1, 1}, {625, 266}}</string>
+									<reference key="NSSuperview" ref="248496456"/>
+									<reference key="NSWindow"/>
+									<reference key="NSNextKeyView" ref="746145959"/>
+									<string key="NSReuseIdentifierKey">_NS:11</string>
+									<reference key="NSDocView" ref="746145959"/>
+									<reference key="NSBGColor" ref="640036751"/>
+									<object class="NSCursor" key="NSCursor">
+										<string key="NSHotSpot">{4, 5}</string>
+										<object class="NSImage" key="NSImage">
+											<int key="NSImageFlags">79691776</int>
+											<array key="NSReps">
+												<array>
+													<integer value="5"/>
+													<object class="NSURL">
+														<nil key="NS.base"/>
+														<string key="NS.relative">file://localhost/Applications/zXcode_4.6.3.app/Contents/SharedFrameworks/DVTKit.framework/Resources/DVTIbeamCursor.tiff</string>
+													</object>
+												</array>
+											</array>
+											<object class="NSColor" key="NSColor">
+												<int key="NSColorSpace">3</int>
+												<bytes key="NSWhite">MCAwAA</bytes>
+											</object>
+										</object>
+									</object>
+									<int key="NScvFlags">4</int>
+								</object>
+								<object class="NSScroller" id="992720392">
+									<reference key="NSNextResponder" ref="248496456"/>
+									<int key="NSvFlags">256</int>
+									<string key="NSFrame">{{626, 1}, {15, 266}}</string>
+									<reference key="NSSuperview" ref="248496456"/>
+									<reference key="NSWindow"/>
+									<reference key="NSNextKeyView" ref="268801594"/>
+									<string key="NSReuseIdentifierKey">_NS:83</string>
+									<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+									<reference key="NSTarget" ref="248496456"/>
+									<string key="NSAction">_doScroller:</string>
+									<double key="NSCurValue">1</double>
+									<double key="NSPercent">0.85256409645080566</double>
+								</object>
+								<object class="NSScroller" id="424197942">
+									<reference key="NSNextResponder" ref="248496456"/>
+									<int key="NSvFlags">-2147483392</int>
+									<string key="NSFrame">{{-100, -100}, {87, 18}}</string>
+									<reference key="NSSuperview" ref="248496456"/>
+									<reference key="NSWindow"/>
+									<reference key="NSNextKeyView" ref="455232561"/>
+									<string key="NSReuseIdentifierKey">_NS:33</string>
+									<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+									<int key="NSsFlags">1</int>
+									<reference key="NSTarget" ref="248496456"/>
+									<string key="NSAction">_doScroller:</string>
+									<double key="NSCurValue">1</double>
+									<double key="NSPercent">0.94565218687057495</double>
+								</object>
+							</array>
+							<string key="NSFrame">{{-2, -1}, {642, 268}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView" ref="424197942"/>
+							<string key="NSReuseIdentifierKey">_NS:9</string>
+							<int key="NSsFlags">133138</int>
+							<reference key="NSVScroller" ref="992720392"/>
+							<reference key="NSHScroller" ref="424197942"/>
+							<reference key="NSContentView" ref="455232561"/>
+							<double key="NSMinMagnification">0.25</double>
+							<double key="NSMaxMagnification">4</double>
+							<double key="NSMagnification">1</double>
+						</object>
+						<object class="NSButton" id="931890582">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">268</int>
+							<string key="NSFrame">{{8, 275}, {86, 18}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView" ref="711969047"/>
+							<string key="NSReuseIdentifierKey">_NS:9</string>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="382435753">
+								<int key="NSCellFlags">-2080374784</int>
+								<int key="NSCellFlags2">268435456</int>
+								<string key="NSContents">Compress</string>
+								<object class="NSFont" key="NSSupport" id="839552639">
+									<string key="NSName">LucidaGrande</string>
+									<double key="NSSize">13</double>
+									<int key="NSfFlags">1044</int>
+								</object>
+								<string key="NSCellIdentifier">_NS:9</string>
+								<reference key="NSControlView" ref="931890582"/>
+								<int key="NSButtonFlags">1210863872</int>
+								<int key="NSButtonFlags2">2</int>
+								<object class="NSCustomResource" key="NSNormalImage">
+									<string key="NSClassName">NSImage</string>
+									<string key="NSResourceName">NSSwitch</string>
+								</object>
+								<object class="NSButtonImageSource" key="NSAlternateImage">
+									<string key="NSImageName">NSSwitch</string>
+								</object>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+							<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+						</object>
+						<object class="NSTextField" id="711969047">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">268</int>
+							<string key="NSFrame">{{98, 277}, {93, 17}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView" ref="699751417"/>
+							<string key="NSReuseIdentifierKey">_NS:1535</string>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="951805185">
+								<int key="NSCellFlags">68157504</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents">Output name</string>
+								<reference key="NSSupport" ref="839552639"/>
+								<string key="NSCellIdentifier">_NS:1535</string>
+								<reference key="NSControlView" ref="711969047"/>
+								<object class="NSColor" key="NSBackgroundColor" id="253962311">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">controlColor</string>
+									<reference key="NSColor" ref="426337731"/>
+								</object>
+								<object class="NSColor" key="NSTextColor" id="290465496">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">controlTextColor</string>
+									<reference key="NSColor" ref="971210461"/>
+								</object>
+							</object>
+							<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+							<int key="NSTextFieldAlignmentRectInsetsVersion">1</int>
+						</object>
+						<object class="NSTextField" id="699751417">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">268</int>
+							<string key="NSFrame">{{191, 275}, {231, 22}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView" ref="328931960"/>
+							<string key="NSReuseIdentifierKey">_NS:9</string>
+							<string key="NSHuggingPriority">{1000, 750}</string>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="786656625">
+								<int key="NSCellFlags">-1804599231</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents"/>
+								<reference key="NSSupport" ref="839552639"/>
+								<string key="NSCellIdentifier">_NS:9</string>
+								<reference key="NSControlView" ref="699751417"/>
+								<bool key="NSDrawsBackground">YES</bool>
+								<object class="NSColor" key="NSBackgroundColor">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">textBackgroundColor</string>
+									<reference key="NSColor" ref="640036751"/>
+								</object>
+								<object class="NSColor" key="NSTextColor">
+									<int key="NSColorSpace">6</int>
+									<string key="NSCatalogName">System</string>
+									<string key="NSColorName">textColor</string>
+									<reference key="NSColor" ref="971210461"/>
+								</object>
+							</object>
+							<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+							<int key="NSTextFieldAlignmentRectInsetsVersion">1</int>
+						</object>
+						<object class="NSTextField" id="328931960">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">268</int>
+							<string key="NSFrame">{{433, 277}, {90, 17}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView" ref="248496456"/>
+							<string key="NSReuseIdentifierKey">_NS:1535</string>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSTextFieldCell" key="NSCell" id="617713799">
+								<int key="NSCellFlags">68157504</int>
+								<int key="NSCellFlags2">272630784</int>
+								<string key="NSContents">Output folder</string>
+								<reference key="NSSupport" ref="839552639"/>
+								<string key="NSCellIdentifier">_NS:1535</string>
+								<reference key="NSControlView" ref="328931960"/>
+								<reference key="NSBackgroundColor" ref="253962311"/>
+								<reference key="NSTextColor" ref="290465496"/>
+							</object>
+							<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+							<int key="NSTextFieldAlignmentRectInsetsVersion">1</int>
+						</object>
+						<object class="NSButton" id="268801594">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">266</int>
+							<string key="NSFrame">{{516, 266}, {125, 32}}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<reference key="NSWindow"/>
+							<reference key="NSNextKeyView"/>
+							<string key="NSReuseIdentifierKey">_NS:9</string>
+							<string key="NSHuggingPriority">{250, 1}</string>
+							<bool key="NSEnabled">YES</bool>
+							<object class="NSButtonCell" key="NSCell" id="771251508">
+								<int key="NSCellFlags">67108864</int>
+								<int key="NSCellFlags2">134217728</int>
+								<string key="NSContents">Button</string>
+								<reference key="NSSupport" ref="839552639"/>
+								<string key="NSCellIdentifier">_NS:9</string>
+								<reference key="NSControlView" ref="268801594"/>
+								<int key="NSButtonFlags">-2038284288</int>
+								<int key="NSButtonFlags2">129</int>
+								<string key="NSAlternateContents"/>
+								<string key="NSKeyEquivalent"/>
+								<int key="NSPeriodicDelay">200</int>
+								<int key="NSPeriodicInterval">25</int>
+							</object>
+							<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
+						</object>
+					</array>
+					<string key="NSFrameSize">{640, 300}</string>
+					<reference key="NSSuperview"/>
+					<reference key="NSWindow"/>
+					<reference key="NSNextKeyView" ref="931890582"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1280, 778}}</string>
+				<string key="NSMinSize">{640, 142}</string>
+				<string key="NSMaxSize">{10000000000000, 10000000000000}</string>
+				<bool key="NSWindowIsRestorable">YES</bool>
+			</object>
+			<object class="NSCustomObject" id="976324537">
+				<string key="NSClassName">AppDelegate</string>
+			</object>
+			<object class="NSCustomObject" id="755631768">
+				<string key="NSClassName">NSFontManager</string>
+			</object>
+		</array>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<array class="NSMutableArray" key="connectionRecords">
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1050"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">449</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="238522557"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="976324537"/>
+					</object>
+					<int key="connectionID">495</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="860595796"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="583158037"/>
+					</object>
+					<int key="connectionID">232</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="755159360"/>
+					</object>
+					<int key="connectionID">367</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="342932134"/>
+					</object>
+					<int key="connectionID">368</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="908899353"/>
+					</object>
+					<int key="connectionID">370</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="972006081"/>
+						<reference key="destination" ref="1021"/>
+					</object>
+					<int key="connectionID">555</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="972006081"/>
+					</object>
+					<int key="connectionID">532</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">compressCheckClick:</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="931890582"/>
+					</object>
+					<int key="connectionID">551</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">compressCheck</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="931890582"/>
+					</object>
+					<int key="connectionID">556</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">outputFilenameEdit</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="699751417"/>
+					</object>
+					<int key="connectionID">571</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">outputFolderClick:</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="268801594"/>
+					</object>
+					<int key="connectionID">691</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">folderButton</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="268801594"/>
+					</object>
+					<int key="connectionID">720</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">par2niiClick:</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="482520206"/>
+					</object>
+					<int key="connectionID">774</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">dicom2niiClick:</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="722745758"/>
+					</object>
+					<int key="connectionID">775</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">theTextView</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="746145959"/>
+					</object>
+					<int key="connectionID">798</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="746145959"/>
+						<reference key="destination" ref="1021"/>
+					</object>
+					<int key="connectionID">791</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBAccessibilityConnection" key="connection">
+						<string key="label">link</string>
+						<reference key="source" ref="711969047"/>
+						<reference key="destination" ref="699751417"/>
+					</object>
+					<int key="connectionID">693</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBAccessibilityConnection" key="connection">
+						<string key="label">link</string>
+						<reference key="source" ref="711969047"/>
+						<reference key="destination" ref="699751417"/>
+					</object>
+					<int key="connectionID">694</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="699751417"/>
+						<reference key="destination" ref="976324537"/>
+					</object>
+					<int key="connectionID">583</int>
+				</object>
+			</array>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<array key="orderedObjects">
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<array key="object" id="0"/>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="694149608"/>
+							<reference ref="952259628"/>
+							<reference ref="379814623"/>
+						</array>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="110575045"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="952259628"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="789758025"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="379814623"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="720053764"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">81</int>
+						<reference key="object" ref="720053764"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="722745758"/>
+							<reference ref="482520206"/>
+						</array>
+						<reference key="parent" ref="379814623"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">72</int>
+						<reference key="object" ref="722745758"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="789758025"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="583158037"/>
+							<reference ref="860595796"/>
+						</array>
+						<reference key="parent" ref="952259628"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="583158037"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="860595796"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="238522557"/>
+							<reference ref="755159360"/>
+							<reference ref="908899353"/>
+							<reference ref="632727374"/>
+							<reference ref="646227648"/>
+							<reference ref="609285721"/>
+							<reference ref="481834944"/>
+							<reference ref="304266470"/>
+							<reference ref="1046388886"/>
+							<reference ref="1056857174"/>
+							<reference ref="342932134"/>
+						</array>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="238522557"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="755159360"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="908899353"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="646227648"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="609285721"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="481834944"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="304266470"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1046388886"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="752062318"/>
+						</array>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="1056857174"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="342932134"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="752062318"/>
+						<reference key="parent" ref="1046388886"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="972006081"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="439893737"/>
+						</array>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">372</int>
+						<reference key="object" ref="439893737"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="931890582"/>
+							<reference ref="711969047"/>
+							<reference ref="699751417"/>
+							<reference ref="328931960"/>
+							<reference ref="248496456"/>
+							<reference ref="268801594"/>
+						</array>
+						<reference key="parent" ref="972006081"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">420</int>
+						<reference key="object" ref="755631768"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">494</int>
+						<reference key="object" ref="976324537"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">536</int>
+						<reference key="object" ref="248496456"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="746145959"/>
+							<reference ref="424197942"/>
+							<reference ref="992720392"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">537</int>
+						<reference key="object" ref="746145959"/>
+						<reference key="parent" ref="248496456"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">538</int>
+						<reference key="object" ref="424197942"/>
+						<reference key="parent" ref="248496456"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">539</int>
+						<reference key="object" ref="992720392"/>
+						<reference key="parent" ref="248496456"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">547</int>
+						<reference key="object" ref="931890582"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="382435753"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">548</int>
+						<reference key="object" ref="382435753"/>
+						<reference key="parent" ref="931890582"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">557</int>
+						<reference key="object" ref="711969047"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="951805185"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">558</int>
+						<reference key="object" ref="951805185"/>
+						<reference key="parent" ref="711969047"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">564</int>
+						<reference key="object" ref="699751417"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="786656625"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">565</int>
+						<reference key="object" ref="786656625"/>
+						<reference key="parent" ref="699751417"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">701</int>
+						<reference key="object" ref="328931960"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="617713799"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">702</int>
+						<reference key="object" ref="617713799"/>
+						<reference key="parent" ref="328931960"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">685</int>
+						<reference key="object" ref="268801594"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="771251508"/>
+						</array>
+						<reference key="parent" ref="439893737"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">686</int>
+						<reference key="object" ref="771251508"/>
+						<reference key="parent" ref="268801594"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">773</int>
+						<reference key="object" ref="482520206"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+				</array>
+			</object>
+			<dictionary class="NSMutableDictionary" key="flattenedProperties">
+				<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="129.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="130.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="131.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="134.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="144.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="145.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="149.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="197.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="198.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="205.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="236.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<boolean value="YES" key="371.IBNSWindowAutoPositionCentersHorizontal"/>
+				<boolean value="YES" key="371.IBNSWindowAutoPositionCentersVertical"/>
+				<string key="371.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="371.IBWindowTemplateEditedContentRect">{{380, 496}, {480, 360}}</string>
+				<integer value="1" key="371.NSWindowTemplate.visibleAtLaunch"/>
+				<string key="372.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="420.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="494.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="536.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="537.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="538.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="539.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSMutableDictionary" key="547.IBAttributePlaceholdersKey">
+					<string key="NS.key.0">ToolTip</string>
+					<object class="IBToolTipAttribute" key="NS.object.0">
+						<string key="name">ToolTip</string>
+						<reference key="object" ref="931890582"/>
+						<string key="toolTip">If checked, images will be stored as compressed .nii.gz</string>
+					</object>
+				</object>
+				<string key="547.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="548.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="557.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="558.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSMutableDictionary" key="564.IBAttributePlaceholdersKey">
+					<string key="NS.key.0">ToolTip</string>
+					<object class="IBToolTipAttribute" key="NS.object.0">
+						<string key="name">ToolTip</string>
+						<reference key="object" ref="699751417"/>
+						<string key="toolTip">Name of NIfTI images (%c= comments, %f folder name, %i ID of patient %n name of patient, %p protocol name, %s series, %t time)</string>
+					</object>
+				</object>
+				<string key="564.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="565.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSMutableDictionary" key="685.IBAttributePlaceholdersKey">
+					<string key="NS.key.0">ToolTip</string>
+					<object class="IBToolTipAttribute" key="NS.object.0">
+						<string key="name">ToolTip</string>
+						<reference key="object" ref="268801594"/>
+						<string key="toolTip">Set location for converted images (or choose to have files saved to the same location as the input images)</string>
+					</object>
+				</object>
+				<string key="685.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="686.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="701.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="702.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="773.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="81.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="83.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			</dictionary>
+			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+			<nil key="activeLocalization"/>
+			<dictionary class="NSMutableDictionary" key="localizations"/>
+			<nil key="sourceID"/>
+			<int key="maxID">815</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<array class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<object class="IBPartialClassDescription">
+					<string key="className">AppDelegate</string>
+					<string key="superclassName">NSObject</string>
+					<dictionary class="NSMutableDictionary" key="actions">
+						<string key="compressCheckClick:">id</string>
+						<string key="dicom2niiClick:">id</string>
+						<string key="outputFolderClick:">id</string>
+						<string key="par2niiClick:">id</string>
+					</dictionary>
+					<dictionary class="NSMutableDictionary" key="actionInfosByName">
+						<object class="IBActionInfo" key="compressCheckClick:">
+							<string key="name">compressCheckClick:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+						<object class="IBActionInfo" key="dicom2niiClick:">
+							<string key="name">dicom2niiClick:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+						<object class="IBActionInfo" key="outputFolderClick:">
+							<string key="name">outputFolderClick:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+						<object class="IBActionInfo" key="par2niiClick:">
+							<string key="name">par2niiClick:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</dictionary>
+					<dictionary class="NSMutableDictionary" key="outlets">
+						<string key="compressCheck">NSButton</string>
+						<string key="folderButton">NSButton</string>
+						<string key="outputFilenameEdit">NSTextField</string>
+						<string key="theTextView">NSTextView</string>
+						<string key="window">NSWindow</string>
+					</dictionary>
+					<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<object class="IBToOneOutletInfo" key="compressCheck">
+							<string key="name">compressCheck</string>
+							<string key="candidateClassName">NSButton</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="folderButton">
+							<string key="name">folderButton</string>
+							<string key="candidateClassName">NSButton</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="outputFilenameEdit">
+							<string key="name">outputFilenameEdit</string>
+							<string key="candidateClassName">NSTextField</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="theTextView">
+							<string key="name">theTextView</string>
+							<string key="candidateClassName">NSTextView</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="window">
+							<string key="name">window</string>
+							<string key="candidateClassName">NSWindow</string>
+						</object>
+					</dictionary>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/AppDelegate.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">myWindow</string>
+					<string key="superclassName">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/myWindow.h</string>
+					</object>
+				</object>
+			</array>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+			<real value="1060" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="4600" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<string key="NSMenuCheckmark">{11, 11}</string>
+			<string key="NSMenuMixedState">{10, 3}</string>
+			<string key="NSSwitch">{15, 15}</string>
+		</dictionary>
+	</data>
+</archive>
diff --git a/xcode/dcm2/main.m b/xcode/dcm2/main.m
new file mode 100644
index 0000000..27390f1
--- /dev/null
+++ b/xcode/dcm2/main.m
@@ -0,0 +1,14 @@
+//
+//  main.m
+//  dcm2
+//
+//  Created by Chris Rorden on 4/7/14.
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+    return NSApplicationMain(argc, (const char **)argv);
+}
diff --git a/xcode/dcm2/myWindow.h b/xcode/dcm2/myWindow.h
new file mode 100644
index 0000000..cbfeba3
--- /dev/null
+++ b/xcode/dcm2/myWindow.h
@@ -0,0 +1,13 @@
+//
+//  myWindow.h
+//  dcm2
+//
+//  Created by Chris Rorden on 4/7/14.
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+ at interface myWindow : NSWindow
+
+ at end
diff --git a/xcode/dcm2/myWindow.m b/xcode/dcm2/myWindow.m
new file mode 100644
index 0000000..c67adc3
--- /dev/null
+++ b/xcode/dcm2/myWindow.m
@@ -0,0 +1,46 @@
+//
+//  myWindow.m
+//  dcm2
+//
+//  Created by Chris Rorden on 4/7/14.
+//  Copyright (c) 2014 Chris Rorden. All rights reserved.
+//
+
+#import "myWindow.h"
+#import "AppDelegate.h"
+
+ at implementation myWindow
+
+- (void)awakeFromNib {
+    //http://stackoverflow.com/questions/8567348/accepting-dragged-files-on-a-cocoa-application
+    [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+}
+
+
+- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender {
+    return NSDragOperationCopy;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
+    return NSDragOperationCopy;
+}
+
+-(BOOL)canBecomeKeyWindow
+{
+    return YES;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
+    NSPasteboard *pboard = [sender draggingPasteboard];
+    NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
+    if (1 == filenames.count)
+        if ([[NSApp delegate] respondsToSelector:@selector(application:openFile:)]) {
+            //return [[NSApp delegate] application:NSApp openFile:[filenames lastObject]];
+            [(AppDelegate *)[NSApp  delegate] processFile:[filenames lastObject]];
+            return TRUE;
+        }
+    
+    return FALSE;
+}
+
+ at end
diff --git a/xcode/dcm2niigui.icns b/xcode/dcm2niigui.icns
new file mode 100644
index 0000000..e4301de
Binary files /dev/null and b/xcode/dcm2niigui.icns differ

-- 
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