├── .gitignore ├── LICENSE ├── CMakeLists.txt ├── decode_notes.md ├── pugiconfig.hpp ├── IsmrmrdPhilips.xsl ├── philips.h ├── README.md ├── main.cpp └── pugixml.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | #* 3 | build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | PHILIPS TO ISMRMRD CONVERTER LICENSE V1.0, OCTOBER 2015 2 | 3 | PERMISSION IS HEREBY GRANTED, FREE OF CHARGE, TO ANY PERSON OBTAINING 4 | A COPY OF THIS SOFTWARE AND ASSOCIATED DOCUMENTATION FILES (THE 5 | "SOFTWARE"), TO DEAL IN THE SOFTWARE WITHOUT RESTRICTION, INCLUDING 6 | WITHOUT LIMITATION THE RIGHTS TO USE, COPY, DISTRIBUTE, SUBLICENSE, 7 | AND/OR SELL COPIES OF THE SOFTWARE, AND TO PERMIT PERSONS TO WHOM THE 8 | SOFTWARE IS FURNISHED TO DO SO, SUBJECT TO THE FOLLOWING CONDITIONS: 9 | 10 | THE ABOVE COPYRIGHT NOTICE, THIS PERMISSION NOTICE, AND THE LIMITATION 11 | OF LIABILITY BELOW SHALL BE INCLUDED IN ALL COPIES OR REDISTRIBUTIONS 12 | OF SUBSTANTIAL PORTIONS OF THE SOFTWARE. 13 | 14 | SOFTWARE IS BEING DEVELOPED IN PART AT THE NATIONAL HEART, LUNG, AND BLOOD 15 | INSTITUTE, NATIONAL INSTITUTES OF HEALTH BY AN EMPLOYEE OF THE FEDERAL 16 | GOVERNMENT IN THE COURSE OF HIS OFFICIAL DUTIES. PURSUANT TO TITLE 17, 17 | SECTION 105 OF THE UNITED STATES CODE, THIS SOFTWARE IS NOT SUBJECT TO 18 | COPYRIGHT PROTECTION AND IS IN THE PUBLIC DOMAIN. EXCEPT AS CONTAINED IN 19 | THIS NOTICE, THE NAME OF THE AUTHORS, THE NATIONAL HEART, LUNG, AND BLOOD 20 | INSTITUTE (NHLBI), OR THE NATIONAL INSTITUTES OF HEALTH (NIH) MAY NOT 21 | BE USED TO ENDORSE OR PROMOTE PRODUCTS DERIVED FROM THIS SOFTWARE WITHOUT 22 | SPECIFIC PRIOR WRITTEN PERMISSION FROM THE NHLBI OR THE NIH.THE SOFTWARE IS 23 | PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 24 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 25 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 27 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 28 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | THE SOFTWARE WAS NOT DEVELOPED BY PHILIPS (IN WHOLE OR IN PART). PHILIPS IS 30 | NOT RESPONSIBLE FOR THE CONTENTS OR FUNCTIONALITY OF THE SOFTWARE. 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(PHILIPS-TO-ISMRMRD) 4 | 5 | #VERSIONING 6 | set(PHILIPS_TO_ISMRMRD_VERSION_MAJOR 0) 7 | set(PHILIPS_TO_ISMRMRD_VERSION_MINOR 0) 8 | set(PHILIPS_TO_ISMRMRD_VERSION_PATCH 1) 9 | set(PHILIPS_TO_ISMRMRD_VERSION_STRING ${PHILIPS_TO_ISMRMRD_VERSION_MAJOR}.${PHILIPS_TO_ISMRMRD_VERSION_MINOR}.${PHILIPS_TO_ISMRMRD_VERSION_PATCH}) 10 | 11 | if (WIN32) 12 | ADD_DEFINITIONS(-DWIN32 -D_WIN32 -D_WINDOWS) 13 | # ADD_DEFINITIONS(-DUNICODE -D_UNICODE) 14 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /MP") 15 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3") 16 | set (CMAKE_EXE_LINKER_FLAGS_DEBUG "/debug /INCREMENTAL:NO") 17 | set (CMAKE_SHARED_LINKER_FLAGS_DEBUG "/debug /INCREMENTAL:NO") 18 | set (CMAKE_STATIC_LINKER_FLAGS_DEBUG "/debug /INCREMENTAL:NO") 19 | set (CMAKE_MODULE_LINKER_FLAGS_DEBUG "/debug /INCREMENTAL:NO") 20 | 21 | #ADD_DEFINITIONS(-D__BUILD_CONVERTER_TINYXML__) 22 | else () 23 | if (UNIX) 24 | if (APPLE) 25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 26 | else () 27 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++11") 28 | endif () 29 | endif () 30 | endif () 31 | 32 | # Added for static linking!!! 33 | set(Boost_USE_STATIC_LIBS ON) 34 | find_package(Boost COMPONENTS thread system program_options filesystem REQUIRED) 35 | 36 | find_package(LibXml2 REQUIRED) 37 | find_package(LibXslt REQUIRED) 38 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR} ${LIBXSLT_INCLUDE_DIR}) 39 | 40 | find_package(ISMRMRD REQUIRED) 41 | find_package(HDF5 1.8 REQUIRED COMPONENTS C) 42 | INCLUDE_DIRECTORIES( ${ISMRMRD_INCLUDE_DIR} ${HDF5_C_INCLUDE_DIR}) 43 | link_directories( ${ISMRMRD_LIB_DIR} ) 44 | 45 | add_executable(philips_to_ismrmrd 46 | main.cpp 47 | pugixml.cpp) 48 | 49 | target_link_libraries(philips_to_ismrmrd ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES} ${ISMRMRD_LIBRARIES} ${Boost_LIBRARIES}) 50 | 51 | install(TARGETS philips_to_ismrmrd DESTINATION bin) 52 | -------------------------------------------------------------------------------- /decode_notes.md: -------------------------------------------------------------------------------- 1 | For each label, if the data is encoded (`raw_format` is `4` or `6`), read `coded_data_size` bytes from the file, create a buffer of size `data_size` bytes, and pass both to the `decode` function. 2 | 3 | Acquisitions are encoded individually. Each encoded acquisition is made up of a series of chunks. A chunk contains a header and corresponding data. The header is comprised of: 4 | 5 | - 2-byte integer - decoded size in bytes of chunk data 6 | - 2-byte integer - encoded size in bytes of chunk data 7 | - 4-byte integer - destination of decoded data as an offset in bytes into the decoded acquisition buffer 8 | 9 | The decoded size of a chunk appears to be a multiple of the number of samples for a single coil, etc, often just 1 line, but not always. 10 | Example with `ky,kx = 178,480`: 11 | 12 | acq # | size | # chunks | decoded chunk size | avg. encoded chunk size 13 | ------|-------|----------|--------------------|------------------------ 14 | 0 | 49920 | 26 | 1920 | 386.462 15 | 1 | 49920 | 13 | 3840 | 766.154 16 | 2 | 49920 | 26 | 1920 | 389.692 17 | 3 | 49920 | 26 | 1920 | 391.692 18 | 19 | A chunk is decoded by repeating the following process until every encoded integer in the chunk is decoded (`nelements = decoded_size / 4-bytes per integer`): 20 | 21 | 1. Decode 5-bit integer from buffer. This will be the bit-resolution. 22 | 1. Decode 5-bit integer from buffer. This will be the optional shift value (commonly zero) 23 | 1. Calculate an addend using the shift value: `(unsigned)(1 << shift) >> 1`, which is *essentially* `2 ^ (shift - 1)` but handles the case where `shift = -1`. 24 | 1. Repeat the following 16 times (or fewer if there are less than 16 encoded integers remaining): 25 | 26 | 1. Decode an `n`-bit integer `x` from buffer, where `n = bit-resolution`. 27 | 1. Left-shift `x` by `shift` and add the `addend`: `x = (x << shift) + addend`. 28 | 1. Save `x` as a decoded 32-bit integer. 29 | 30 | Decoding an `n`-bit integer from the buffer is achieved by keeping track of two values: 31 | 32 | 1. the "current" bit-position, and 33 | 2. a "current" 32-bit integer 34 | 35 | The bit-position starts at zero for each chunk. The first decoding requires reading an entire 32-bit integer from the encoded buffer. This is saved as the "current" value and the bit-resolution is subtracted from the bit-position. The *next* decoding will either extract the next `n`-bit integer (`n = bit-resolution`) from the "current" value, or, if the bit-position is less than the bit-resolution, read another 32-bit integer from the encoded buffer, binary `or` it with the remaining "current" value, and update the "current" value and bit-position accordingly. 36 | -------------------------------------------------------------------------------- /pugiconfig.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.4 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at http://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef HEADER_PUGICONFIG_HPP 15 | #define HEADER_PUGICONFIG_HPP 16 | 17 | // Uncomment this to enable wchar_t mode 18 | // #define PUGIXML_WCHAR_MODE 19 | 20 | // Uncomment this to disable XPath 21 | // #define PUGIXML_NO_XPATH 22 | 23 | // Uncomment this to disable STL 24 | // #define PUGIXML_NO_STL 25 | 26 | // Uncomment this to disable exceptions 27 | // #define PUGIXML_NO_EXCEPTIONS 28 | 29 | // Set this to control attributes for public classes/functions, i.e.: 30 | // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL 31 | // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL 32 | // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall 33 | // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead 34 | 35 | // Tune these constants to adjust memory-related behavior 36 | // #define PUGIXML_MEMORY_PAGE_SIZE 32768 37 | // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 38 | // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 39 | 40 | // Uncomment this to switch to header-only version 41 | // #define PUGIXML_HEADER_ONLY 42 | // #include "pugixml.cpp" 43 | 44 | // Uncomment this to enable long long support 45 | // #define PUGIXML_HAS_LONG_LONG 46 | 47 | #endif 48 | 49 | /** 50 | * Copyright (c) 2006-2014 Arseny Kapoulkine 51 | * 52 | * Permission is hereby granted, free of charge, to any person 53 | * obtaining a copy of this software and associated documentation 54 | * files (the "Software"), to deal in the Software without 55 | * restriction, including without limitation the rights to use, 56 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 57 | * copies of the Software, and to permit persons to whom the 58 | * Software is furnished to do so, subject to the following 59 | * conditions: 60 | * 61 | * The above copyright notice and this permission notice shall be 62 | * included in all copies or substantial portions of the Software. 63 | * 64 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 65 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 66 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 67 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 68 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 69 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 70 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 71 | * OTHER DEALINGS IN THE SOFTWARE. 72 | */ 73 | -------------------------------------------------------------------------------- /IsmrmrdPhilips.xsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | STUDY 22 | PATIENT 23 | 24 | _ 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | HFS 37 | 38 | 39 | 40 | 41 | 42 | 43 | PHILIPS 44 | 0.793 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 0 60 | 61 | 62 | 63 | cartesian 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 0 117 | 118 | 119 | 120 |
121 | 122 |
123 |
124 | 125 | 0 126 | 127 | 128 | 129 |
130 | 131 |
132 |
133 | 134 | 0 135 | 136 | 137 | 138 |
0
139 |
140 | 141 | 0 142 | 143 | 144 | 145 |
0
146 |
147 | 148 | 0 149 | 150 | 151 | 152 |
0
153 |
154 | 155 | 0 156 | 157 | 158 | 159 |
0
160 |
161 | 162 | 0 163 | 0 164 |
0
165 |
166 | 167 | 0 168 | 169 | 170 | 171 |
0
172 |
173 | 174 | 0 175 | 176 | 177 | 178 |
0
179 |
180 |
181 |
182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 |
206 |
207 |
208 | -------------------------------------------------------------------------------- /philips.h: -------------------------------------------------------------------------------- 1 | #ifndef PHILIPS_H 2 | #define PHILIPS_H 3 | /********************************************************************************** 4 | 5 | Header file describing the layout of the Philips MRI raw data label files (*.lab) 6 | 7 | The information in this header has been obtained by reverse engineering 8 | using a hex editor and the output readPhilipsExports module and associated 9 | ReadPhilips node in the GPI framework. Those tools can be obtained from: 10 | https://github.com/gpilab/philips-data-reader 11 | 12 | Michael S. Hansen (michael.hansen@nih.gov) 13 | April 2015 14 | 15 | ***********************************************************************************/ 16 | namespace philips 17 | { 18 | 19 | /* 20 | 21 | #Reverse engineered from GPI module readPhilipsExports 22 | #In Python: 23 | 24 | import readPhilipsExports as rp 25 | for l in range(100000): 26 | if rp.LabelTypeEnum(l) != 'LABEL_TYPE_MAX': 27 | print str(rp.LabelTypeEnum(l)) + " = " + str(hex(l)) 28 | */ 29 | typedef enum 30 | { 31 | LABEL_TYPE_MIN = 0x7f00, 32 | LABEL_TYPE_STANDARD = 0x7f01, 33 | LABEL_TYPE_IGEO = 0x7f02, 34 | LABEL_TYPE_IGEO_PMC = 0x7f03, 35 | LABEL_TYPE_COIL_POS = 0x7f04, 36 | LABEL_TYPE_NON_LIN = 0x7f05, 37 | LABEL_TYPE_MAX 38 | } label_types; 39 | 40 | 41 | /* 42 | #Reverse engineered from GPI module readPhilipsExports 43 | #In Python: 44 | 45 | import readPhilipsExports as rp 46 | for l in range(100000): 47 | if rp.CtrlEnum(l) != 'CTRL_MAX': 48 | print str(rp.CtrlEnum(l)) + " = " + str(hex(l)) + "," 49 | */ 50 | typedef enum 51 | { 52 | CTRL_NORMAL_DATA = 0x0, 53 | CTRL_DC_OFFSET_DATA = 0x1, 54 | CTRL_JUNK_DATA = 0x2, 55 | CTRL_ECHO_PHASE_DATA = 0x3, 56 | CTRL_NO_DATA = 0x4, 57 | CTRL_NEXT_PHASE = 0x5, 58 | CTRL_SUSPEND = 0x6, 59 | CTRL_RESUME = 0x7, 60 | CTRL_TOTAL_END = 0x8, 61 | CTRL_INVALIDATION = 0x9, 62 | CTRL_TYPE_NR_END = 0xa, 63 | CTRL_VALIDATION = 0xb, 64 | CTRL_NO_OPERATION = 0xc, 65 | CTRL_DYN_SCAN_INFO = 0xd, 66 | CTRL_SELECTIVE_END = 0xe, 67 | CTRL_FRC_CH_DATA = 0xf, 68 | CTRL_FRC_NOISE_DATA = 0x10, 69 | CTRL_REFERENCE_DATA = 0x11, 70 | CTRL_DC_FIXED_DATA = 0x12, 71 | CTRL_NAVIGATOR_DATA = 0x13, 72 | CTRL_FLUSH = 0x14, 73 | CTRL_RECON_END = 0x15, 74 | CTRL_IMAGE_STATUS = 0x16, 75 | CTRL_TRACKING = 0x17, 76 | CTRL_FLUOROSCOPY_TOGGLE = 0x18, 77 | CTRL_REJECTED_DATA = 0x19, 78 | CTRL_PROGRESS_INFO = 0x1a, 79 | CTRL_END_PREP_PHASE = 0x1b, 80 | CTRL_CHANNEL_DEFINITION = 0x1c, 81 | CTRL_MAX 82 | } ctrl_types; 83 | 84 | 85 | /* 86 | 87 | The structure of the label (*.lab) files has been reverse engineered in the following way: 88 | 89 | With access to two different versions of the files (an older and a newer) it was determined that there are two different formats. 90 | 91 | The readPhilipsExports Python Module from GPI was used find a) the names of the header fields and b) the location of the header fields. 92 | The readPhilipsExports module has a function: 93 | 94 | readLab(filename, isMira) 95 | Parse a *.lab header accompanying the *.raw data file. 96 | 97 | The isMira argument is apparently used to indicate if this is a new header (True) or an old one (False). 98 | 99 | Both new and old headers are 64 bytes long. 100 | 101 | By visual inspection of the result of reading new and old files using this function, the location of two header fields 102 | "data_size" and "label_type" was found. These fields are located in the same bytes in the new and old header. 103 | 104 | A file with test labels was then generated using the following function: 105 | 106 | int generate_test_lab(const char* filename) 107 | { 108 | typedef struct 109 | { 110 | uint32_t data_size; 111 | char test_bank1[10]; 112 | uint16_t label_type; 113 | char test_bank2[48]; 114 | } test_label; 115 | 116 | test_label l; 117 | 118 | std::ofstream f(filename, std::ofstream::binary); 119 | 120 | if (!f.is_open()) { 121 | std::cout << "Error opening file" << std::endl; 122 | return -1; 123 | } 124 | 125 | for (size_t i = 0; i < 10; ++i) { 126 | memset(&l,0,sizeof(test_label)); 127 | l.label_type = philips::LABEL_TYPE_STANDARD; 128 | l.test_bank1[i] = 1; 129 | f.write(reinterpret_cast(&l),sizeof(test_label)); 130 | } 131 | 132 | for (size_t i = 0; i < 48; ++i) { 133 | memset(&l,0,sizeof(test_label)); 134 | l.label_type = philips::LABEL_TYPE_STANDARD; 135 | l.test_bank2[i] = 1; 136 | f.write(reinterpret_cast(&l),sizeof(test_label)); 137 | } 138 | 139 | f.close(); 140 | return 0; 141 | } 142 | 143 | 144 | The test label file was then read in python with code like: 145 | 146 | #Python script for inspecting test labels 147 | 148 | import readPhilipsExports as rp 149 | 150 | lab = rp.rp.readLab('test.lab', True) #For new label layout 151 | lab = rp.rp.readLab('test.lab', False) #For old label layout 152 | 153 | #End of Python script for inspecting test labels 154 | 155 | For example to figure out which bytes contribute to the echo_nr field, simply inspect: 156 | 157 | lab['echo_nr'] 158 | 159 | and find non-zero entries. 160 | 161 | The names of the header fields are listed below. 162 | 163 | Old header field: 164 | ['control', 'grad_echo_nr', 'rr_interval', 'echo_nr', 'cardiac_phase_nr', 'dst_code', 'leading_dummies_size', 'monitoring_flag', 'e2_profile_nr', 'row_nr', 'trailing_dummies_size', 'label_type', 'raw_format', 'src_code', 'rf_echo_nr', 'rtop_offset', 'dynamic_scan_nr', 'random_phase', 'location_nr', 'data_size', 'gain_setting_index', 'e3_profile_nr', 'progress_cnt', 'measurement_sign', 'channels_active', 'enc_time', 'measurement_phase', 'extra_attr_nr', 'e1_profile_nr', 'measurement_nr', 'mix_nr', 'seq_nr', 'spare_1'] 165 | 166 | New header fields: 167 | ['control', 'grad_echo_nr', 'rr_interval', 'echo_nr', 'cardiac_phase_nr', 'measurement_nr', 'monitoring_flag', 'e2_profile_nr', 'row_nr', 'label_type', 'e1_profile_nr', 'rf_echo_nr', 'rtop_offset', 'dynamic_scan_nr', 'random_phase', 'location_nr', 'data_size', 'gain_setting_index', 'e3_profile_nr', 'progress_cnt', 'measurement_sign', 'coded_data_size', 'channels_active', 'enc_time', 'measurement_phase', 'extra_attr_nr', 'normalization_factor', 'raw_format', 'mix_nr', 'seq_nr', 'spare_1'] 168 | */ 169 | 170 | typedef struct 171 | { 172 | uint32_t data_size; 173 | uint32_t coded_data_size; 174 | uint16_t src_code; 175 | uint16_t dst_code; 176 | uint16_t seq_nr; 177 | uint16_t label_type; 178 | char control; 179 | char monitoring_flag; 180 | char measurement_phase; 181 | char measurement_sign; 182 | char gain_setting_index; 183 | char spare_1; 184 | uint16_t spare_2; 185 | uint16_t progress_cnt; 186 | uint16_t mix_nr; 187 | uint16_t dynamic_scan_nr; 188 | uint16_t cardiac_phase_nr; 189 | uint16_t echo_nr; 190 | uint16_t location_nr; 191 | uint16_t row_nr; 192 | uint16_t extra_attr_nr; 193 | uint16_t measurement_nr; 194 | uint16_t e1_profile_nr; 195 | uint16_t e2_profile_nr; 196 | uint16_t e3_profile_nr; 197 | uint16_t rf_echo_nr; 198 | uint16_t grad_echo_nr; 199 | uint16_t enc_time; 200 | uint16_t random_phase; 201 | uint16_t rr_interval; 202 | uint16_t rtop_offset; 203 | uint32_t channels_active; 204 | } old_label; 205 | 206 | typedef struct 207 | { 208 | uint32_t data_size; 209 | uint32_t coded_data_size; 210 | float normalization_factor; 211 | uint16_t seq_nr; 212 | uint16_t label_type; 213 | char control; 214 | char monitoring_flag; 215 | char measurement_phase; 216 | char measurement_sign; 217 | char gain_setting_index; 218 | char raw_format; 219 | uint16_t spare_1; 220 | uint16_t progress_cnt; 221 | uint16_t mix_nr; 222 | uint16_t dynamic_scan_nr; 223 | uint16_t cardiac_phase_nr; 224 | uint16_t echo_nr; 225 | uint16_t location_nr; 226 | uint16_t row_nr; 227 | uint16_t extra_attr_nr; 228 | uint16_t measurement_nr; 229 | uint16_t e1_profile_nr; 230 | uint16_t e2_profile_nr; 231 | uint16_t e3_profile_nr; 232 | uint16_t rf_echo_nr; 233 | uint16_t grad_echo_nr; 234 | uint16_t enc_time; 235 | uint16_t random_phase; 236 | uint16_t rr_interval; 237 | uint16_t rtop_offset; 238 | uint32_t channels_active; 239 | } new_label; //Total size 64 bytes 240 | 241 | typedef struct 242 | { 243 | union 244 | { 245 | old_label old_; 246 | new_label new_; 247 | }; 248 | } label; //Total size 64 bytes 249 | 250 | } 251 | 252 | #endif //PHILIPS_H 253 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![DOI](https://zenodo.org/badge/15627/ismrmrd/philips_to_ismrmrd.svg)](https://zenodo.org/badge/latestdoi/15627/ismrmrd/philips_to_ismrmrd) 2 | 3 | # philips_to_ismrmrd 4 | 5 | A tool for converting Philips raw MR data into [ISMRMRD](https://ismrmrd.github.io) format. 6 | 7 | ## Dependencies 8 | 9 | - [ISMRMRD](https://github.com/ismrmrd/ismrmrd) 10 | - [CMake](http://www.cmake.org/) for building 11 | - [Boost](http://www.boost.org/) 12 | - [Libxslt](http://xmlsoft.org/libxslt/) 13 | - [Libxml2](http://xmlsoft.org/) 14 | 15 | ## Build 16 | 17 | ### Mac OS X 18 | 19 | 1. Install dependencies 20 | - Example commands to acquire dependencies and build `ismrmrd` using `brew` tested on Mac OS X 10.10.5 Yosemite, XCode version 2339 21 | * If you already have `brew`, you should make sure it is up-to-date 22 | 23 | ``` 24 | brew update 25 | ``` 26 | 27 | * Otherwise, install `brew` 28 | 29 | ``` 30 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 31 | ``` 32 | 33 | * Then install `ismrmrd` dependencies 34 | 35 | ``` 36 | brew install wget cmake doxygen boost fftw 37 | ``` 38 | 39 | * If you have the `anaconda` distribution of `python`, you should already have `hdf5`, but you should make sure everything is up-to-date 40 | 41 | ``` 42 | conda update 43 | ``` 44 | 45 | * Otherise, let `brew` install `hdf5` 46 | 47 | ``` 48 | brew tap homebrew/science 49 | brew install homebrew/science/hdf5 50 | ``` 51 | 52 | * Download, compile and install `ismrmrd` 53 | 54 | ``` 55 | git clone https://github.com/ismrmrd/ismrmrd 56 | cd ismrmrd 57 | mkdir build 58 | cd build 59 | cmake .. 60 | make install 61 | ``` 62 | 63 | 2. Get the code: 64 | 65 | ``` 66 | git clone https://github.com/ismrmrd/philips_to_ismrmrd 67 | ``` 68 | 69 | 3. Generate build files, compile, and install: 70 | 71 | ``` 72 | cd philips_to_ismrmrd 73 | mkdir build 74 | cd build 75 | cmake .. 76 | make install 77 | ``` 78 | 79 | + If `cmake` complains about missing `libxml2` and/or `libxslt`, you can use `brew` to fix it 80 | 81 | ``` 82 | brew install libxml2 83 | brew link --force libxml2 84 | ``` 85 | 86 | + and/or 87 | 88 | ``` 89 | brew install libxslt 90 | brew link --force libxslt 91 | ``` 92 | + then try again 93 | 94 | ``` 95 | cmake .. 96 | make install 97 | ``` 98 | 99 | ### Linux 100 | 101 | 1. Install dependencies 102 | - Example commands to acquire dependencies and build `ismrmrd` using `apt-get` tested on Ubuntu 14.04.3 LTS 103 | 104 | ``` 105 | sudo apt-get update 106 | sudo apt-get install build-essential git cmake cmake-qt-gui doxygen libboost-all-dev fftw-dev libhdf5-serial-dev hdf5-tools 107 | git clone https://github.com/ismrmrd/ismrmrd 108 | cd ismrmrd 109 | mkdir build 110 | cd build 111 | cmake .. 112 | make 113 | sudo make install 114 | sudo ldconfig 115 | ``` 116 | - Example commands to acquire `philips_to_ismrmrd` dependencies `libxml` and `libxslt` using `apt-get` tested on Ubuntu 14.04.3 LTS 117 | 118 | ``` 119 | sudo apt-get install libxml2-dev libxslt1-dev 120 | ``` 121 | 122 | 2. Get the code: 123 | 124 | ``` 125 | git clone https://github.com/ismrmrd/philips_to_ismrmrd 126 | ``` 127 | 128 | 3. Generate build files, compile, and install: 129 | 130 | ``` 131 | cd philips_to_ismrmrd 132 | mkdir build 133 | cd build 134 | cmake .. 135 | sudo make install 136 | ``` 137 | 138 | ### Windows 7 (tested with Visual Studio Pro 2013) 139 | 1. Compile and install 32-bit version (not 64-bit version) of `ismrmrd` following steps inspired by instructions found [here](http://sourceforge.net/p/gadgetron/home/Windows%20Installation/) 140 | - Add `C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin` to system `path` environment variable 141 | - Copy 32-bit [FFTW3 binaries](http://www.fftw.org/install/windows.html) to `C:\Libraries\FFTW3` 142 | - Create FFTW3 .lib files on the command line with: 143 | 144 | ``` 145 | c:\Libraries\FFTW3>lib /machine:x86 /def:libfftw3f-3.def 146 | c:\Libraries\FFTW3>lib /machine:x86 /def:libfftw3-3.def 147 | c:\Libraries\FFTW3>lib /machine:x86 /def:libfftw3l-3.def 148 | ``` 149 | 150 | - Add `C:\Libraries\FFTW3` to the system path environment variable 151 | - Install `boost_1_55_0-msvc-12.0-32.exe` available [here](http://sourceforge.net/projects/boost/files/boost-binaries/1.55.0/) to location `C:\Libraries\boost_1_55_0` 152 | - Install `hdf5-1.8.15-win32-vs2013-shared.zip` available [here](https://www.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8.15/bin/windows/) to location `C:\local\HDF_Group\HDF5\1.8.15\` 153 | - Install CMake available [here](http://www.cmake.org/files/v3.3/cmake-3.3.0-win32-x86.exe) and add to system `path` environment variable 154 | - Get the `ismrmrd` code using `git clone https://github.com/ismrmrd/ismrmrd` or download zip and save to a parent folder located at `C:\Libraries\ismrmrd` 155 | - Create folder `C:\Libraries\ismrmrd\build` 156 | - Set the following system environment variables (forward slashes, no quotes) 157 | 158 | ``` 159 | FFTW3_ROOT_DIR = C:/Libraries/FFTW3 160 | BOOST_ROOT = C:/Libraries/boost_1_55_0 161 | BOOST_LIBRARYDIR = C:/Libraries/boost_1_55_0/lib32-msvc-12.0 162 | HDF5_DIR = C:/local/HDF_Group/HDF5/1.8.15/cmake 163 | ``` 164 | 165 | - Run CMake GUI with following entries 166 | 167 | ``` 168 | Where is the source code: C:/Libraries/ismrmrd 169 | Where to build the binaries: C:/Libraries/ismrmrd/build 170 | ``` 171 | 172 | - Click CMake Configure button and select options 173 | 174 | ``` 175 | Generator: Visual Studio 12 2013 176 | Use default native compilers 177 | ``` 178 | 179 | - Open `C:\Libraries\ismrmrd\build\ISMRMRD.sln` created by CMake 180 | - Change from `Debug` to `Release` 181 | - In Solution Explorer window, right-click `ismrmrd` and select `properties -> C/C++ -> General -> Additional Include Directories` and insert `C:\local\HDF_Group\HDF5\1.8.15\include` 182 | - Using a text editor outside Visual Studio, replace all occurrences of `bin\hdf5.dll` with `lib\hdf5.lib` in `./ismrmrd.vcxproj`, `./examples/c/ismrmrd_c_example.vcxproj`, `./utilities/ismrmrd_generate_cartesian_shepp_logan.vcxproj`, `./utilities/ismrmrd_info.vcxproj`, `./utilities/ismrmrd_read_timing_test.vcxproj`, `./utilities/ismrmrd_recon_cartesian_2d.vcxproj` 183 | - Return to Visual Studio, select "Reload All" to load altered .vcxproj files 184 | - Select `BUILD -> Build Solution` 185 | - Install NSIS Windows installer creator available [here](http://nsis.sourceforge.net/Download) 186 | - Create a `README.html` file for packaging 187 | 188 | ``` 189 | copy C:\Libraries\ismrmrd\README.md C:\Libraries\ismrmrd\README.html 190 | ``` 191 | - Edit `C:\Libraries\ismrmrd\build\CPackConfig.cmake` and replace all occurrences of `"C:/Program Files (x86)/ISMRMRD"` with `"/ISMRMRD"` 192 | 193 | - Create installer package from the command line 194 | 195 | ``` 196 | cd C:\Libraries\ismrmrd\build 197 | cpack 198 | ``` 199 | 200 | - Run installer created at location `C:/Libraries/ismrmrd/build/ismrmrd-1.3.2.exe` and select add ISMRMRD to path for all users and install to parent folder `C:\local\ismrmrd` 201 | 202 | - Add `C:\local\ismrmrd\ISMRMRD\lib` to the system path environment variable 203 | 204 | 2. Download `philips_to_ismrmrd` dependencies `libxml` and `libxslt` 205 | - Download `iconv-1.9.2.win32.zip`, `libxml2-2.6.30.win32.zip`, `libxslt-1.1.26.win32.zip` and `zlib-1.2.5.win32.zip` available [here](http://xmlsoft.org/sources/win32/) and install to `C:\Libraries\iconv-1.9.2.win32`, `C:\Libraries\libxml2-2.6.30.win32`, `C:\Libraries\libxslt-1.1.26.win32` and `C:\Libraries\zlib-1.2.5` respectively 206 | - Add `C:\Libraries\iconv-1.9.2.win32\bin;C:\Libraries\libxml2-2.6.30.win32\bin;C:\Libraries\libxslt-1.1.26.win32\bin;C:\Libraries\zlib-1.2.5\bin` to the system path environment variable 207 | 208 | 3. Get the `philips_to_ismrmrd` code using `git clone https://github.com/ismrmrd/philips_to_ismrmrd` or download zip with parent folder located at `C:\Libraries\philips_to_ismrmrd` 209 | 210 | 4. Prepare to compile from the command-line 211 | 212 | ``` 213 | cd "c:\Program Files (x86)\Microsoft Visual Studio 12.0\VC" 214 | vcvarsall x86 215 | ``` 216 | 217 | 5. Compile `philips_to_ismrmrd.exe` 218 | 219 | ``` 220 | cd c:\Libraries\philips_to_ismrmrd 221 | cl main.cpp pugixml.cpp /EHa /Fephilips_to_ismrmrd.exe /GS /TP /W3 /Zc:wchar_t- /Gm- /O2 /Ob2 /D "WIN32" /D "_WINDOWS" /D "NDEBUG" /D "_WIN32" /D "UNICODE" /D "_UNICODE" /D "_CRT_SECURE_NO_WARNINGS" /D "__func__=__FUNCTION__" /WX- /Zc:forScope /Gd /MD /I"C:/Libraries/iconv-1.9.2.win32/include/" /I"C:/Libraries/libxml2-2.6.30.win32/include/" /I"C:/Libraries/boost_1_55_0/" /I"C:/Libraries/libxml2-2.6.30.win32/include/" /I"C:/Libraries/libxslt-1.1.26.win32/include/" /I"C:/local/ismrmrd/ISMRMRD/include/" /link /libpath:"C:/Libraries/libxml2-2.6.30.win32/lib/" libxml2.lib /libpath:"C:/Libraries/libxslt-1.1.26.win32/lib/" libxslt.lib /libpath:"C:/local/ismrmrd/ISMRMRD/lib/" ismrmrd.lib /libpath:"C:/Libraries/boost_1_55_0/lib32-msvc-12.0/" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "comdlg32.lib" "advapi32.lib" 222 | ``` 223 | 224 | 6. Use `philips_to_ismrmrd.exe` on another Windows 7 computer by gathering the following files into a single folder to copy to the other computer. Alternatively, use the [philips_to_ismrmrd_exe](https://github.com/welcheb/philips_to_ismrmrd_exe) tool designed for use on a Philips MRI Scanner console running Windows 7 (Philips MRI software release 5 or newer) 225 | 226 | ``` 227 | C:\Libraries\philips_to_ismrmrd\philips_to_ismrmrd.exe 228 | C:\Libraries\philips_to_ismrmrd\IsmrmrdPhilips.xsl 229 | C:\local\HDF_Group\HDF5\1.8.15\bin\hdf5.dll 230 | C:\Libraries\iconv-1.9.2.win32\bin\iconv.dll 231 | C:\local\ismrmrd\ISMRMRD\lib\ismrmrd.dll 232 | C:\Libraries\libxml2-2.6.30.win32\bin\libxml2.dll 233 | C:\Libraries\libxslt-1.1.26.win32\bin\libxslt.dll 234 | C:\local\ismrmrd\ISMRMRD\bin\mscvcp120.dll 235 | C:\local\ismrmrd\ISMRMRD\bin\msvcr120.dll 236 | C:\local\HDF_Group\HDF5\1.8.15\bin\szip.dll 237 | C:\local\HDF_Group\HDF5\1.8.15\bin\zlib.dll 238 | C:\Libraries\zlib-1.2.5\bin\zlib1.dll 239 | ``` 240 | 241 | ## Running the converter 242 | 243 | ### Simple conversion: 244 | 245 | ``` 246 | philips_to_ismrmrd -f -x IsmrmrdPhilips.xsl -o philips.h5 247 | ``` 248 | 249 | ### Conversion with schema validation: 250 | 251 | ``` 252 | philips_to_ismrmrd -f -x IsmrmrdPhilips.xsl -s -o philips.h5 253 | ``` 254 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "pugixml.hpp" 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ismrmrd/ismrmrd.h" 26 | #include "ismrmrd/dataset.h" 27 | #include "ismrmrd/version.h" 28 | 29 | #include 30 | namespace po = boost::program_options; 31 | 32 | #include 33 | 34 | #include "philips.h" 35 | 36 | #ifndef M_PI 37 | #define M_PI 3.14159265358979323846264338327950288 38 | #endif 39 | 40 | struct sinparms 41 | { 42 | sinparms() 43 | : ismira(true) 44 | , nchan(1) 45 | { 46 | 47 | } 48 | 49 | bool ismira; 50 | uint16_t nchan; 51 | std::vector< std::complex > pda_amp_factors; 52 | }; 53 | 54 | void sintoxml(std::ostream& s, const char* filename, sinparms& sp) 55 | { 56 | std::ifstream f(filename); 57 | if (!f.is_open()) { 58 | throw std::runtime_error("Unable to open SIN file for XML conversion"); 59 | } 60 | 61 | pugi::xml_document doc; 62 | pugi::xml_node root = doc.append_child(); 63 | root.set_name("philips"); 64 | 65 | std::string line; 66 | const size_t buffer_size = 4096; 67 | char buffer[buffer_size]; 68 | while (!f.eof()) { 69 | f.getline(buffer,buffer_size); 70 | std::string line(buffer); 71 | std::stringstream s; 72 | uint16_t idx1, idx2, idx3; 73 | char tmp; 74 | std::string parameter_name; 75 | std::string parameter_value; 76 | if (line.find(':') != std::string::npos && isdigit(line[1])) { 77 | boost::algorithm::trim_right(line); 78 | s << line; 79 | s >> idx1; s >> idx2; s >> idx3; s >> tmp; 80 | s >> parameter_name; 81 | 82 | pugi::xml_node parm = root.append_child(parameter_name.c_str()); 83 | pugi::xml_attribute attr_idx1 = parm.append_attribute("idx1"); 84 | attr_idx1.set_value(idx1); 85 | pugi::xml_attribute attr_idx2 = parm.append_attribute("idx2"); 86 | attr_idx2.set_value(idx2); 87 | pugi::xml_attribute attr_idx3 = parm.append_attribute("idx3"); 88 | attr_idx3.set_value(idx3); 89 | s >> tmp; 90 | 91 | bool get_nchan = false; 92 | float pda[2]; 93 | bool get_pda = false; 94 | int pda_comp = 0; 95 | if (parameter_name == "enable_pda") { 96 | sp.ismira = false; 97 | } else if (parameter_name == "max_measured_channels") { 98 | get_nchan = true; 99 | } else if (parameter_name == "nr_measured_channels") { 100 | get_nchan = true; 101 | } else if (parameter_name == "pda_ampl_factors") { 102 | get_pda = true; 103 | } 104 | 105 | while (!s.eof()) { 106 | s >> parameter_value; 107 | 108 | if (get_nchan) { 109 | sp.nchan = std::atoi(parameter_value.c_str()); 110 | } 111 | 112 | if (get_pda) { 113 | pda[pda_comp++] = std::atof(parameter_value.c_str()); 114 | 115 | if (pda_comp > 1) { 116 | sp.pda_amp_factors.push_back(std::complex(pda[0],pda[1])); 117 | pda_comp = 0; 118 | } 119 | } 120 | 121 | pugi::xml_node v = parm.append_child("value"); 122 | v.append_child(pugi::node_pcdata).set_value(parameter_value.c_str()); 123 | } 124 | } 125 | } 126 | 127 | f.close(); 128 | doc.save(s); 129 | } 130 | 131 | int xml_file_is_valid(std::string& xml, std::string& schema_file) 132 | { 133 | xmlDocPtr doc; 134 | //parse an XML in-memory block and build a tree. 135 | doc = xmlParseMemory(xml.c_str(), xml.size()); 136 | 137 | xmlDocPtr schema_doc; 138 | //parse an XML in-memory block and build a tree. 139 | schema_doc = xmlParseMemory(schema_file.c_str(), schema_file.size()); 140 | 141 | //Create an XML Schemas parse context for that document. NB. The document may be modified during the parsing process. 142 | xmlSchemaParserCtxtPtr parser_ctxt = xmlSchemaNewDocParserCtxt(schema_doc); 143 | if (parser_ctxt == NULL) 144 | { 145 | /* unable to create a parser context for the schema */ 146 | xmlFreeDoc(schema_doc); 147 | return -2; 148 | } 149 | 150 | //parse a schema definition resource and build an internal XML Shema struture which can be used to validate instances. 151 | xmlSchemaPtr schema = xmlSchemaParse(parser_ctxt); 152 | if (schema == NULL) 153 | { 154 | /* the schema itself is not valid */ 155 | xmlSchemaFreeParserCtxt(parser_ctxt); 156 | xmlFreeDoc(schema_doc); 157 | return -3; 158 | } 159 | 160 | //Create an XML Schemas validation context based on the given schema. 161 | xmlSchemaValidCtxtPtr valid_ctxt = xmlSchemaNewValidCtxt(schema); 162 | if (valid_ctxt == NULL) 163 | { 164 | /* unable to create a validation context for the schema */ 165 | xmlSchemaFree(schema); 166 | xmlSchemaFreeParserCtxt(parser_ctxt); 167 | xmlFreeDoc(schema_doc); 168 | xmlFreeDoc(doc); 169 | return -4; 170 | } 171 | 172 | //Validate a document tree in memory. Takes a schema validation context and a parsed document tree 173 | int is_valid = (xmlSchemaValidateDoc(valid_ctxt, doc) == 0); 174 | xmlSchemaFreeValidCtxt(valid_ctxt); 175 | xmlSchemaFree(schema); 176 | xmlSchemaFreeParserCtxt(parser_ctxt); 177 | xmlFreeDoc(schema_doc); 178 | xmlFreeDoc(doc); 179 | 180 | /* force the return value to be non-negative on success */ 181 | return is_valid ? 1 : 0; 182 | } 183 | 184 | std::string apply_stylesheet(std::string xml_raw, std::string xsl) 185 | { 186 | xsltStylesheetPtr cur = NULL; 187 | xmlDocPtr doc, res, xml_doc; 188 | const char *params[16 + 1]; 189 | int nbparams = 0; 190 | params[nbparams] = NULL; 191 | xmlSubstituteEntitiesDefault(1); 192 | xmlLoadExtDtdDefaultValue = 1; 193 | 194 | xml_doc = xmlParseMemory(xsl.c_str(), xsl.size()); 195 | 196 | if (xml_doc == NULL) { 197 | throw std::runtime_error("Error when parsing xsl parameter stylesheet"); 198 | } 199 | 200 | cur = xsltParseStylesheetDoc(xml_doc); 201 | doc = xmlParseMemory(xml_raw.c_str(), xml_raw.size()); 202 | res = xsltApplyStylesheet(cur, doc, params); 203 | 204 | xmlChar* out_ptr = NULL; 205 | int xslt_length = 0; 206 | int xslt_result = xsltSaveResultToString(&out_ptr, &xslt_length, res, cur); 207 | 208 | if (xslt_result < 0) { 209 | throw std::runtime_error("Failed to save converted doc to string"); 210 | } 211 | 212 | std::string xml_out((char*)out_ptr,xslt_length); 213 | 214 | xsltFreeStylesheet(cur); 215 | xmlFreeDoc(res); 216 | xmlFreeDoc(doc); 217 | 218 | xsltCleanupGlobals(); 219 | xmlCleanupParser(); 220 | 221 | return xml_out; 222 | } 223 | 224 | 225 | template T read(uint8_t** bufptr) 226 | { 227 | T t = *(reinterpret_cast(*bufptr)); 228 | *bufptr += sizeof(T); 229 | return t; 230 | } 231 | 232 | int32_t decode_int(uint8_t** buffer, int32_t bitres, bool sign, int32_t& val, int32_t& bitpos) { 233 | int32_t tmp = 0; 234 | const int32_t NUMBITS = 32; 235 | 236 | if (bitpos == 0) { 237 | val = read(buffer); 238 | tmp = val; 239 | bitpos = NUMBITS - bitres; 240 | } else { 241 | tmp = val << (NUMBITS - bitpos); 242 | if (bitpos >= bitres) { 243 | bitpos -= bitres; 244 | } else { 245 | val = read(buffer); 246 | tmp |= (uint32_t)val >> bitpos; 247 | bitpos += NUMBITS - bitres; 248 | } 249 | } 250 | 251 | if (sign) { 252 | return tmp >> (NUMBITS - bitres); 253 | } else { 254 | return (uint32_t)tmp >> (NUMBITS - bitres); 255 | } 256 | } 257 | 258 | void decode_chunk(uint8_t* chunk_buffer, int32_t* decoded_buffer, size_t nelements) 259 | { 260 | int32_t val = 0, bitpos = 0; 261 | int idx = 0; 262 | while (idx < nelements) { 263 | int32_t bitres = decode_int(&chunk_buffer, 5, false, val, bitpos); 264 | //printf("-- val: %d, bitpos: %d, bitres: %d\n", val, bitpos, bitres); 265 | 266 | // `shift` is *usually* == 0 267 | int32_t shift = decode_int(&chunk_buffer, 5, false, val, bitpos); 268 | //printf("-- val: %d, bitpos: %d, shift: %d\n", val, bitpos, shift); 269 | 270 | // `addend` is *usually* == 0 271 | uint32_t addend = (uint32_t)(1 << shift) >> 1; 272 | 273 | for (int n = std::min((int)nelements - idx, 16); n > 0; n--) { 274 | int32_t tmp = decode_int(&chunk_buffer, bitres, true, val, bitpos); 275 | decoded_buffer[idx++] = (tmp << shift) + addend; 276 | } 277 | } 278 | } 279 | 280 | void decode(uint8_t* encoded_buffer, int32_t* decoded_buffer, uint32_t decoded_data_size) 281 | { 282 | size_t num_decoded_bytes = 0; 283 | while (num_decoded_bytes < decoded_data_size) { 284 | uint16_t expected_decoded_size = read(&encoded_buffer); 285 | uint16_t expected_encoded_size = read(&encoded_buffer); 286 | uint32_t decoded_buffer_offset = read(&encoded_buffer) / 4; 287 | 288 | size_t nelements = expected_decoded_size / 4; 289 | int32_t* output_buffer = decoded_buffer + decoded_buffer_offset; 290 | decode_chunk(encoded_buffer, output_buffer, nelements); 291 | num_decoded_bytes += expected_decoded_size; 292 | encoded_buffer += expected_encoded_size; 293 | } 294 | } 295 | 296 | int main(int argc, char** argv) 297 | { 298 | 299 | std::string filename; 300 | std::string parammap_xsl; 301 | std::string ismrmrd_schema; 302 | 303 | std::string ismrmrd_file; 304 | std::string ismrmrd_group; 305 | 306 | bool debug_xml = false; 307 | bool header_only = false; 308 | 309 | const float polar_magnitude = 1.0; 310 | float polar_phase = 0.0; 311 | 312 | po::options_description desc("Allowed options"); 313 | desc.add_options() 314 | ("help,h", "Produce HELP message") 315 | ("file,f", po::value(&filename), "") 316 | ("pMapStyle,x", po::value(¶mmap_xsl), "") 317 | ("schema,s", po::value(&ismrmrd_schema), " (for validation)") 318 | ("output,o", po::value(&ismrmrd_file)->default_value("output.h5"), "") 319 | ("outputGroup,g", po::value(&ismrmrd_group)->default_value("dataset"), "") 320 | ("debug,X", po::value(&debug_xml)->implicit_value(true), "") 321 | ("headerOnly,H", po::value(&header_only)->implicit_value(true), "
") 322 | ; 323 | 324 | po::variables_map vm; 325 | 326 | try 327 | { 328 | po::store(po::parse_command_line(argc, argv, desc), vm); 329 | po::notify(vm); 330 | 331 | if (vm.count("help")) 332 | { 333 | std::cout << desc << "\n"; 334 | return 1; 335 | } 336 | } catch(po::error& e) { 337 | std::cerr << "ERROR: " << e.what() << std::endl << std::endl; 338 | std::cerr << desc << std::endl; 339 | return -1; 340 | } 341 | 342 | std::string lab_filename = filename + std::string(".lab"); 343 | std::string raw_filename = filename + std::string(".raw"); 344 | std::string sin_filename = filename + std::string(".sin"); 345 | 346 | //Let's process the sin file first 347 | sinparms sp; 348 | std::stringstream s; 349 | std::string ismrmrd_xml; 350 | std::string xsl_string; 351 | std::ifstream fxsl(parammap_xsl.c_str()); 352 | if (!fxsl) { 353 | std::cerr << "Parameter XSL stylesheet: " << parammap_xsl << " does not exist." << std::endl; 354 | std::cerr << desc << "\n"; 355 | return -1; 356 | } else { 357 | std::string str_f((std::istreambuf_iterator(fxsl)), std::istreambuf_iterator()); 358 | xsl_string = str_f; 359 | } 360 | fxsl.close(); 361 | 362 | try { 363 | sintoxml(s,sin_filename.c_str(), sp); 364 | ismrmrd_xml = apply_stylesheet(s.str(), xsl_string); 365 | if (debug_xml) { 366 | std::ofstream rf("raw.xml"); 367 | rf.write(s.str().c_str(),s.str().size()); 368 | rf.close(); 369 | std::ofstream pf("processed.xml"); 370 | pf.write(ismrmrd_xml.c_str(),ismrmrd_xml.size()); 371 | pf.close(); 372 | } 373 | } catch (std::runtime_error& e) { 374 | std::cerr << "Failed to convert SIN file to XML: " << e.what() << std::endl; 375 | return -1; 376 | } 377 | 378 | if (vm.count("schema")) { 379 | std::ifstream fxsd(ismrmrd_schema.c_str()); 380 | std::string schema_string; 381 | if (!fxsd) { 382 | std::cerr << "ISMRMRD Schema: " << ismrmrd_schema << " does not exist." << std::endl; 383 | std::cerr << desc << "\n"; 384 | return -1; 385 | } else { 386 | std::string str_f((std::istreambuf_iterator(fxsd)), std::istreambuf_iterator()); 387 | schema_string = str_f; 388 | } 389 | fxsd.close(); 390 | if (!xml_file_is_valid(ismrmrd_xml,schema_string)) { 391 | std::cerr << "Generated ISMRMRD XML header is invalid" << std::endl; 392 | return -1; 393 | } else { 394 | std::cout << "ISMRMRD XML header is valid" << std::endl; 395 | } 396 | } 397 | 398 | std::ifstream labf(lab_filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); //Open file and position at end 399 | 400 | if (!labf.is_open()) { 401 | std::cerr << "Error opening LAB file" << std::endl; 402 | return -1; 403 | } 404 | 405 | std::ifstream raw(raw_filename.c_str(), std::ios::in | std::ios::binary); 406 | 407 | if (!raw.is_open()) { 408 | std::cerr << "Error opening RAW file" << std::endl; 409 | } 410 | 411 | size_t file_length = labf.tellg(); 412 | size_t raw_file_offset = 512;//The first 512 bytes of file appear to be empty 413 | labf.seekg (0, std::ios::beg); 414 | raw.seekg (raw_file_offset, std::ios::beg); 415 | 416 | philips::label l; 417 | 418 | // Create an ISMRMRD dataset 419 | ISMRMRD::Dataset ismrmrd_dataset(ismrmrd_file.c_str(), ismrmrd_group.c_str(), true); 420 | ismrmrd_dataset.writeHeader(ismrmrd_xml); 421 | 422 | while ((file_length - labf.tellg()) >= sizeof(philips::label)) { 423 | labf.read(reinterpret_cast(&l), sizeof(philips::label)); 424 | 425 | if (sp.ismira) { 426 | if (l.new_.label_type > philips::LABEL_TYPE_MIN && l.new_.label_type < philips::LABEL_TYPE_MAX) { 427 | 428 | size_t nchan = sp.nchan; 429 | size_t sample_bytes = 4; // int32... 2 bytes (int16) for non-Mira 430 | size_t nsamp = l.new_.data_size / nchan / 2 / sample_bytes; 431 | 432 | int32_t* converted = new int32_t[l.new_.data_size/4]; 433 | memset(converted, 0, l.new_.data_size); 434 | 435 | size_t bytes_read = 0; 436 | if (l.new_.raw_format == 4 || l.new_.raw_format == 6) { 437 | // need to decode buffer 438 | uint8_t* buffer = new uint8_t[l.new_.coded_data_size]; 439 | raw.read((char*)buffer, l.new_.coded_data_size); 440 | bytes_read = l.new_.coded_data_size; 441 | if (l.new_.control == philips::CTRL_NORMAL_DATA) { 442 | decode(buffer, converted, l.new_.data_size); 443 | } 444 | delete [] buffer; 445 | } else { 446 | raw.read((char*)converted, l.new_.data_size); 447 | bytes_read = l.new_.coded_data_size; 448 | } 449 | 450 | raw_file_offset += bytes_read; 451 | 452 | if (l.new_.label_type == philips::LABEL_TYPE_STANDARD && 453 | ((static_cast(l.new_.control) == philips::CTRL_FRC_NOISE_DATA) || (static_cast(l.new_.control) == philips::CTRL_NORMAL_DATA))) { 454 | 455 | ISMRMRD::Acquisition acq; 456 | acq.clearAllFlags(); 457 | 458 | //TODO: Orientation and position information, should be converted from the SIN file 459 | 460 | if (l.new_.control == philips::CTRL_FRC_NOISE_DATA) { 461 | acq.setFlag(ISMRMRD::ISMRMRD_ACQ_IS_NOISE_MEASUREMENT); 462 | } 463 | 464 | acq.scan_counter() = l.new_.seq_nr; 465 | acq.center_sample() = nsamp>>1; //TODO: Needs to be adjusted for partial Fourier, etc. 466 | 467 | acq.physiology_time_stamp()[0] = l.new_.rtop_offset; 468 | acq.physiology_time_stamp()[1] = l.new_.rr_interval; 469 | 470 | acq.idx().average = l.new_.measurement_nr; 471 | acq.idx().contrast = l.new_.echo_nr; 472 | acq.idx().kspace_encode_step_1 = l.new_.e1_profile_nr; 473 | acq.idx().kspace_encode_step_2 = l.new_.e2_profile_nr; 474 | acq.idx().phase = l.new_.cardiac_phase_nr; 475 | acq.idx().repetition = l.new_.dynamic_scan_nr; 476 | acq.idx().segment = 0; //TODO: Fill in meaningful segment nr 477 | acq.idx().set = l.new_.row_nr; 478 | acq.idx().slice = l.new_.location_nr; 479 | 480 | acq.resize(nsamp, nchan); 481 | std::complex* dptr = acq.getDataPtr(); 482 | 483 | polar_phase = M_PI * (static_cast(l.new_.random_phase) / 32767.0) - 0.5*l.new_.measurement_phase; 484 | std::complex correction_factor = std::polar(polar_magnitude, polar_phase); 485 | 486 | //chop correction, TODO: fix for 3D and add switch for disabling to parameters 487 | if (l.new_.e1_profile_nr%2) correction_factor *= -1.0; 488 | 489 | for (size_t c = 0; c < nchan; c++) { 490 | for (size_t s = 0; s < nsamp; s++) { 491 | dptr[c*nsamp + s] = std::complex(converted[(c*nsamp + s)*2],converted[(c*nsamp + s)*2 + 1]) * correction_factor; 492 | } 493 | } 494 | 495 | ismrmrd_dataset.appendAcquisition(acq); 496 | } 497 | 498 | delete [] converted; 499 | } 500 | } else { 501 | if (l.old_.label_type > philips::LABEL_TYPE_MIN && l.old_.label_type < philips::LABEL_TYPE_MAX) { 502 | 503 | size_t nchan = sp.nchan; 504 | size_t sample_bytes = 2; 505 | size_t nsamp = l.old_.data_size / nchan / 2 / sample_bytes; 506 | 507 | int16_t* converted = new int16_t[l.old_.data_size/2]; 508 | raw.read((char*)converted, l.old_.data_size); 509 | size_t bytes_read = l.old_.coded_data_size; 510 | 511 | raw_file_offset += bytes_read; 512 | 513 | if (l.old_.label_type == philips::LABEL_TYPE_STANDARD && 514 | ((static_cast(l.old_.control) == philips::CTRL_FRC_NOISE_DATA) || (static_cast(l.old_.control) == philips::CTRL_NORMAL_DATA))) { 515 | 516 | ISMRMRD::Acquisition acq; 517 | acq.clearAllFlags(); 518 | 519 | //TODO: Orientation and position information, should be converted from the SIN file 520 | 521 | if (l.old_.control == philips::CTRL_FRC_NOISE_DATA) { 522 | acq.setFlag(ISMRMRD::ISMRMRD_ACQ_IS_NOISE_MEASUREMENT); 523 | } 524 | 525 | acq.scan_counter() = l.old_.seq_nr; 526 | acq.center_sample() = nsamp>>1; //TODO: Needs to be adjusted for partial Fourier, etc. 527 | 528 | acq.idx().average = l.old_.measurement_nr; 529 | acq.idx().contrast = l.old_.echo_nr; 530 | acq.idx().kspace_encode_step_1 = l.old_.e1_profile_nr; 531 | acq.idx().kspace_encode_step_2 = l.old_.e2_profile_nr; 532 | acq.idx().phase = l.old_.cardiac_phase_nr; 533 | acq.idx().repetition = l.old_.dynamic_scan_nr; 534 | acq.idx().segment = 0; //TODO: Fill in meaningful segment nr 535 | acq.idx().set = 0; //TODO: Fill in meaningful set nr 536 | acq.idx().slice = l.old_.location_nr; 537 | 538 | acq.resize(nsamp, nchan); 539 | std::complex* dptr = acq.getDataPtr(); 540 | 541 | polar_phase = M_PI * (static_cast(-1.0*l.old_.random_phase) / 32767.0) - 0.5*l.old_.measurement_phase; 542 | std::complex correction_factor = std::polar(polar_magnitude, polar_phase); 543 | 544 | //chop correction, TODO: fix for 3D and add switch for disabling to parameters 545 | if (l.old_.e1_profile_nr%2) correction_factor *= -1.0; 546 | 547 | for (size_t c = 0; c < nchan; c++) { 548 | for (size_t s = 0; s < nsamp; s++) { 549 | dptr[c*nsamp + s] = std::complex(converted[(c*nsamp + s)*2],converted[(c*nsamp + s)*2 + 1]) * correction_factor; 550 | dptr[c*nsamp + s] *= sp.pda_amp_factors[l.old_.gain_setting_index]; 551 | } 552 | } 553 | 554 | ismrmrd_dataset.appendAcquisition(acq); 555 | } 556 | 557 | delete [] converted; 558 | } 559 | } 560 | } 561 | labf.close(); 562 | raw.close(); 563 | 564 | return 0; 565 | } 566 | -------------------------------------------------------------------------------- /pugixml.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * pugixml parser - version 1.4 3 | * -------------------------------------------------------- 4 | * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) 5 | * Report bugs and download new versions at http://pugixml.org/ 6 | * 7 | * This library is distributed under the MIT License. See notice at the end 8 | * of this file. 9 | * 10 | * This work is based on the pugxml parser, which is: 11 | * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) 12 | */ 13 | 14 | #ifndef PUGIXML_VERSION 15 | // Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons 16 | # define PUGIXML_VERSION 140 17 | #endif 18 | 19 | // Include user configuration file (this can define various configuration macros) 20 | #include "pugiconfig.hpp" 21 | 22 | #ifndef HEADER_PUGIXML_HPP 23 | #define HEADER_PUGIXML_HPP 24 | 25 | // Include stddef.h for size_t and ptrdiff_t 26 | #include 27 | 28 | // Include exception header for XPath 29 | #if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) 30 | # include 31 | #endif 32 | 33 | // Include STL headers 34 | #ifndef PUGIXML_NO_STL 35 | # include 36 | # include 37 | # include 38 | #endif 39 | 40 | // Macro for deprecated features 41 | #ifndef PUGIXML_DEPRECATED 42 | # if defined(__GNUC__) 43 | # define PUGIXML_DEPRECATED __attribute__((deprecated)) 44 | # elif defined(_MSC_VER) && _MSC_VER >= 1300 45 | # define PUGIXML_DEPRECATED __declspec(deprecated) 46 | # else 47 | # define PUGIXML_DEPRECATED 48 | # endif 49 | #endif 50 | 51 | // If no API is defined, assume default 52 | #ifndef PUGIXML_API 53 | # define PUGIXML_API 54 | #endif 55 | 56 | // If no API for classes is defined, assume default 57 | #ifndef PUGIXML_CLASS 58 | # define PUGIXML_CLASS PUGIXML_API 59 | #endif 60 | 61 | // If no API for functions is defined, assume default 62 | #ifndef PUGIXML_FUNCTION 63 | # define PUGIXML_FUNCTION PUGIXML_API 64 | #endif 65 | 66 | // If the platform is known to have long long support, enable long long functions 67 | #ifndef PUGIXML_HAS_LONG_LONG 68 | # if defined(__cplusplus) && __cplusplus >= 201103 69 | # define PUGIXML_HAS_LONG_LONG 70 | # elif defined(_MSC_VER) && _MSC_VER >= 1400 71 | # define PUGIXML_HAS_LONG_LONG 72 | # endif 73 | #endif 74 | 75 | // Character interface macros 76 | #ifdef PUGIXML_WCHAR_MODE 77 | # define PUGIXML_TEXT(t) L ## t 78 | # define PUGIXML_CHAR wchar_t 79 | #else 80 | # define PUGIXML_TEXT(t) t 81 | # define PUGIXML_CHAR char 82 | #endif 83 | 84 | namespace pugi 85 | { 86 | // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE 87 | typedef PUGIXML_CHAR char_t; 88 | 89 | #ifndef PUGIXML_NO_STL 90 | // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE 91 | typedef std::basic_string, std::allocator > string_t; 92 | #endif 93 | } 94 | 95 | // The PugiXML namespace 96 | namespace pugi 97 | { 98 | // Tree node types 99 | enum xml_node_type 100 | { 101 | node_null, // Empty (null) node handle 102 | node_document, // A document tree's absolute root 103 | node_element, // Element tag, i.e. '' 104 | node_pcdata, // Plain character data, i.e. 'text' 105 | node_cdata, // Character data, i.e. '' 106 | node_comment, // Comment tag, i.e. '' 107 | node_pi, // Processing instruction, i.e. '' 108 | node_declaration, // Document declaration, i.e. '' 109 | node_doctype // Document type declaration, i.e. '' 110 | }; 111 | 112 | // Parsing options 113 | 114 | // Minimal parsing mode (equivalent to turning all other flags off). 115 | // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. 116 | const unsigned int parse_minimal = 0x0000; 117 | 118 | // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. 119 | const unsigned int parse_pi = 0x0001; 120 | 121 | // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. 122 | const unsigned int parse_comments = 0x0002; 123 | 124 | // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. 125 | const unsigned int parse_cdata = 0x0004; 126 | 127 | // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. 128 | // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. 129 | const unsigned int parse_ws_pcdata = 0x0008; 130 | 131 | // This flag determines if character and entity references are expanded during parsing. This flag is on by default. 132 | const unsigned int parse_escapes = 0x0010; 133 | 134 | // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. 135 | const unsigned int parse_eol = 0x0020; 136 | 137 | // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. 138 | const unsigned int parse_wconv_attribute = 0x0040; 139 | 140 | // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. 141 | const unsigned int parse_wnorm_attribute = 0x0080; 142 | 143 | // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. 144 | const unsigned int parse_declaration = 0x0100; 145 | 146 | // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. 147 | const unsigned int parse_doctype = 0x0200; 148 | 149 | // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only 150 | // of whitespace is added to the DOM tree. 151 | // This flag is off by default; turning it on may result in slower parsing and more memory consumption. 152 | const unsigned int parse_ws_pcdata_single = 0x0400; 153 | 154 | // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. 155 | const unsigned int parse_trim_pcdata = 0x0800; 156 | 157 | // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document 158 | // is a valid document. This flag is off by default. 159 | const unsigned int parse_fragment = 0x1000; 160 | 161 | // The default parsing mode. 162 | // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, 163 | // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. 164 | const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; 165 | 166 | // The full parsing mode. 167 | // Nodes of all types are added to the DOM tree, character/reference entities are expanded, 168 | // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. 169 | const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; 170 | 171 | // These flags determine the encoding of input data for XML document 172 | enum xml_encoding 173 | { 174 | encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range 235 | { 236 | public: 237 | typedef It const_iterator; 238 | typedef It iterator; 239 | 240 | xml_object_range(It b, It e): _begin(b), _end(e) 241 | { 242 | } 243 | 244 | It begin() const { return _begin; } 245 | It end() const { return _end; } 246 | 247 | private: 248 | It _begin, _end; 249 | }; 250 | 251 | // Writer interface for node printing (see xml_node::print) 252 | class PUGIXML_CLASS xml_writer 253 | { 254 | public: 255 | virtual ~xml_writer() {} 256 | 257 | // Write memory chunk into stream/file/whatever 258 | virtual void write(const void* data, size_t size) = 0; 259 | }; 260 | 261 | // xml_writer implementation for FILE* 262 | class PUGIXML_CLASS xml_writer_file: public xml_writer 263 | { 264 | public: 265 | // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio 266 | xml_writer_file(void* file); 267 | 268 | virtual void write(const void* data, size_t size); 269 | 270 | private: 271 | void* file; 272 | }; 273 | 274 | #ifndef PUGIXML_NO_STL 275 | // xml_writer implementation for streams 276 | class PUGIXML_CLASS xml_writer_stream: public xml_writer 277 | { 278 | public: 279 | // Construct writer from an output stream object 280 | xml_writer_stream(std::basic_ostream >& stream); 281 | xml_writer_stream(std::basic_ostream >& stream); 282 | 283 | virtual void write(const void* data, size_t size); 284 | 285 | private: 286 | std::basic_ostream >* narrow_stream; 287 | std::basic_ostream >* wide_stream; 288 | }; 289 | #endif 290 | 291 | // A light-weight handle for manipulating attributes in DOM tree 292 | class PUGIXML_CLASS xml_attribute 293 | { 294 | friend class xml_attribute_iterator; 295 | friend class xml_node; 296 | 297 | private: 298 | xml_attribute_struct* _attr; 299 | 300 | typedef void (*unspecified_bool_type)(xml_attribute***); 301 | 302 | public: 303 | // Default constructor. Constructs an empty attribute. 304 | xml_attribute(); 305 | 306 | // Constructs attribute from internal pointer 307 | explicit xml_attribute(xml_attribute_struct* attr); 308 | 309 | // Safe bool conversion operator 310 | operator unspecified_bool_type() const; 311 | 312 | // Borland C++ workaround 313 | bool operator!() const; 314 | 315 | // Comparison operators (compares wrapped attribute pointers) 316 | bool operator==(const xml_attribute& r) const; 317 | bool operator!=(const xml_attribute& r) const; 318 | bool operator<(const xml_attribute& r) const; 319 | bool operator>(const xml_attribute& r) const; 320 | bool operator<=(const xml_attribute& r) const; 321 | bool operator>=(const xml_attribute& r) const; 322 | 323 | // Check if attribute is empty 324 | bool empty() const; 325 | 326 | // Get attribute name/value, or "" if attribute is empty 327 | const char_t* name() const; 328 | const char_t* value() const; 329 | 330 | // Get attribute value, or the default value if attribute is empty 331 | const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; 332 | 333 | // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty 334 | int as_int(int def = 0) const; 335 | unsigned int as_uint(unsigned int def = 0) const; 336 | double as_double(double def = 0) const; 337 | float as_float(float def = 0) const; 338 | 339 | #ifdef PUGIXML_HAS_LONG_LONG 340 | long long as_llong(long long def = 0) const; 341 | unsigned long long as_ullong(unsigned long long def = 0) const; 342 | #endif 343 | 344 | // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty 345 | bool as_bool(bool def = false) const; 346 | 347 | // Set attribute name/value (returns false if attribute is empty or there is not enough memory) 348 | bool set_name(const char_t* rhs); 349 | bool set_value(const char_t* rhs); 350 | 351 | // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") 352 | bool set_value(int rhs); 353 | bool set_value(unsigned int rhs); 354 | bool set_value(double rhs); 355 | bool set_value(bool rhs); 356 | 357 | #ifdef PUGIXML_HAS_LONG_LONG 358 | bool set_value(long long rhs); 359 | bool set_value(unsigned long long rhs); 360 | #endif 361 | 362 | // Set attribute value (equivalent to set_value without error checking) 363 | xml_attribute& operator=(const char_t* rhs); 364 | xml_attribute& operator=(int rhs); 365 | xml_attribute& operator=(unsigned int rhs); 366 | xml_attribute& operator=(double rhs); 367 | xml_attribute& operator=(bool rhs); 368 | 369 | #ifdef PUGIXML_HAS_LONG_LONG 370 | xml_attribute& operator=(long long rhs); 371 | xml_attribute& operator=(unsigned long long rhs); 372 | #endif 373 | 374 | // Get next/previous attribute in the attribute list of the parent node 375 | xml_attribute next_attribute() const; 376 | xml_attribute previous_attribute() const; 377 | 378 | // Get hash value (unique for handles to the same object) 379 | size_t hash_value() const; 380 | 381 | // Get internal pointer 382 | xml_attribute_struct* internal_object() const; 383 | }; 384 | 385 | #ifdef __BORLANDC__ 386 | // Borland C++ workaround 387 | bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); 388 | bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); 389 | #endif 390 | 391 | // A light-weight handle for manipulating nodes in DOM tree 392 | class PUGIXML_CLASS xml_node 393 | { 394 | friend class xml_attribute_iterator; 395 | friend class xml_node_iterator; 396 | friend class xml_named_node_iterator; 397 | 398 | protected: 399 | xml_node_struct* _root; 400 | 401 | typedef void (*unspecified_bool_type)(xml_node***); 402 | 403 | public: 404 | // Default constructor. Constructs an empty node. 405 | xml_node(); 406 | 407 | // Constructs node from internal pointer 408 | explicit xml_node(xml_node_struct* p); 409 | 410 | // Safe bool conversion operator 411 | operator unspecified_bool_type() const; 412 | 413 | // Borland C++ workaround 414 | bool operator!() const; 415 | 416 | // Comparison operators (compares wrapped node pointers) 417 | bool operator==(const xml_node& r) const; 418 | bool operator!=(const xml_node& r) const; 419 | bool operator<(const xml_node& r) const; 420 | bool operator>(const xml_node& r) const; 421 | bool operator<=(const xml_node& r) const; 422 | bool operator>=(const xml_node& r) const; 423 | 424 | // Check if node is empty. 425 | bool empty() const; 426 | 427 | // Get node type 428 | xml_node_type type() const; 429 | 430 | // Get node name, or "" if node is empty or it has no name 431 | const char_t* name() const; 432 | 433 | // Get node value, or "" if node is empty or it has no value 434 | // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. 435 | const char_t* value() const; 436 | 437 | // Get attribute list 438 | xml_attribute first_attribute() const; 439 | xml_attribute last_attribute() const; 440 | 441 | // Get children list 442 | xml_node first_child() const; 443 | xml_node last_child() const; 444 | 445 | // Get next/previous sibling in the children list of the parent node 446 | xml_node next_sibling() const; 447 | xml_node previous_sibling() const; 448 | 449 | // Get parent node 450 | xml_node parent() const; 451 | 452 | // Get root of DOM tree this node belongs to 453 | xml_node root() const; 454 | 455 | // Get text object for the current node 456 | xml_text text() const; 457 | 458 | // Get child, attribute or next/previous sibling with the specified name 459 | xml_node child(const char_t* name) const; 460 | xml_attribute attribute(const char_t* name) const; 461 | xml_node next_sibling(const char_t* name) const; 462 | xml_node previous_sibling(const char_t* name) const; 463 | 464 | // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA 465 | const char_t* child_value() const; 466 | 467 | // Get child value of child with specified name. Equivalent to child(name).child_value(). 468 | const char_t* child_value(const char_t* name) const; 469 | 470 | // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) 471 | bool set_name(const char_t* rhs); 472 | bool set_value(const char_t* rhs); 473 | 474 | // Add attribute with specified name. Returns added attribute, or empty attribute on errors. 475 | xml_attribute append_attribute(const char_t* name); 476 | xml_attribute prepend_attribute(const char_t* name); 477 | xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); 478 | xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); 479 | 480 | // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. 481 | xml_attribute append_copy(const xml_attribute& proto); 482 | xml_attribute prepend_copy(const xml_attribute& proto); 483 | xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); 484 | xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); 485 | 486 | // Add child node with specified type. Returns added node, or empty node on errors. 487 | xml_node append_child(xml_node_type type = node_element); 488 | xml_node prepend_child(xml_node_type type = node_element); 489 | xml_node insert_child_after(xml_node_type type, const xml_node& node); 490 | xml_node insert_child_before(xml_node_type type, const xml_node& node); 491 | 492 | // Add child element with specified name. Returns added node, or empty node on errors. 493 | xml_node append_child(const char_t* name); 494 | xml_node prepend_child(const char_t* name); 495 | xml_node insert_child_after(const char_t* name, const xml_node& node); 496 | xml_node insert_child_before(const char_t* name, const xml_node& node); 497 | 498 | // Add a copy of the specified node as a child. Returns added node, or empty node on errors. 499 | xml_node append_copy(const xml_node& proto); 500 | xml_node prepend_copy(const xml_node& proto); 501 | xml_node insert_copy_after(const xml_node& proto, const xml_node& node); 502 | xml_node insert_copy_before(const xml_node& proto, const xml_node& node); 503 | 504 | // Remove specified attribute 505 | bool remove_attribute(const xml_attribute& a); 506 | bool remove_attribute(const char_t* name); 507 | 508 | // Remove specified child 509 | bool remove_child(const xml_node& n); 510 | bool remove_child(const char_t* name); 511 | 512 | // Parses buffer as an XML document fragment and appends all nodes as children of the current node. 513 | // Copies/converts the buffer, so it may be deleted or changed after the function returns. 514 | // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. 515 | xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 516 | 517 | // Find attribute using predicate. Returns first attribute for which predicate returned true. 518 | template xml_attribute find_attribute(Predicate pred) const 519 | { 520 | if (!_root) return xml_attribute(); 521 | 522 | for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) 523 | if (pred(attrib)) 524 | return attrib; 525 | 526 | return xml_attribute(); 527 | } 528 | 529 | // Find child node using predicate. Returns first child for which predicate returned true. 530 | template xml_node find_child(Predicate pred) const 531 | { 532 | if (!_root) return xml_node(); 533 | 534 | for (xml_node node = first_child(); node; node = node.next_sibling()) 535 | if (pred(node)) 536 | return node; 537 | 538 | return xml_node(); 539 | } 540 | 541 | // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. 542 | template xml_node find_node(Predicate pred) const 543 | { 544 | if (!_root) return xml_node(); 545 | 546 | xml_node cur = first_child(); 547 | 548 | while (cur._root && cur._root != _root) 549 | { 550 | if (pred(cur)) return cur; 551 | 552 | if (cur.first_child()) cur = cur.first_child(); 553 | else if (cur.next_sibling()) cur = cur.next_sibling(); 554 | else 555 | { 556 | while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); 557 | 558 | if (cur._root != _root) cur = cur.next_sibling(); 559 | } 560 | } 561 | 562 | return xml_node(); 563 | } 564 | 565 | // Find child node by attribute name/value 566 | xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; 567 | xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; 568 | 569 | #ifndef PUGIXML_NO_STL 570 | // Get the absolute node path from root as a text string. 571 | string_t path(char_t delimiter = '/') const; 572 | #endif 573 | 574 | // Search for a node by path consisting of node names and . or .. elements. 575 | xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; 576 | 577 | // Recursively traverse subtree with xml_tree_walker 578 | bool traverse(xml_tree_walker& walker); 579 | 580 | #ifndef PUGIXML_NO_XPATH 581 | // Select single node by evaluating XPath query. Returns first node from the resulting node set. 582 | xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; 583 | xpath_node select_single_node(const xpath_query& query) const; 584 | 585 | // Select node set by evaluating XPath query 586 | xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; 587 | xpath_node_set select_nodes(const xpath_query& query) const; 588 | #endif 589 | 590 | // Print subtree using a writer object 591 | void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; 592 | 593 | #ifndef PUGIXML_NO_STL 594 | // Print subtree to stream 595 | void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; 596 | void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; 597 | #endif 598 | 599 | // Child nodes iterators 600 | typedef xml_node_iterator iterator; 601 | 602 | iterator begin() const; 603 | iterator end() const; 604 | 605 | // Attribute iterators 606 | typedef xml_attribute_iterator attribute_iterator; 607 | 608 | attribute_iterator attributes_begin() const; 609 | attribute_iterator attributes_end() const; 610 | 611 | // Range-based for support 612 | xml_object_range children() const; 613 | xml_object_range children(const char_t* name) const; 614 | xml_object_range attributes() const; 615 | 616 | // Get node offset in parsed file/string (in char_t units) for debugging purposes 617 | ptrdiff_t offset_debug() const; 618 | 619 | // Get hash value (unique for handles to the same object) 620 | size_t hash_value() const; 621 | 622 | // Get internal pointer 623 | xml_node_struct* internal_object() const; 624 | }; 625 | 626 | #ifdef __BORLANDC__ 627 | // Borland C++ workaround 628 | bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); 629 | bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); 630 | #endif 631 | 632 | // A helper for working with text inside PCDATA nodes 633 | class PUGIXML_CLASS xml_text 634 | { 635 | friend class xml_node; 636 | 637 | xml_node_struct* _root; 638 | 639 | typedef void (*unspecified_bool_type)(xml_text***); 640 | 641 | explicit xml_text(xml_node_struct* root); 642 | 643 | xml_node_struct* _data_new(); 644 | xml_node_struct* _data() const; 645 | 646 | public: 647 | // Default constructor. Constructs an empty object. 648 | xml_text(); 649 | 650 | // Safe bool conversion operator 651 | operator unspecified_bool_type() const; 652 | 653 | // Borland C++ workaround 654 | bool operator!() const; 655 | 656 | // Check if text object is empty 657 | bool empty() const; 658 | 659 | // Get text, or "" if object is empty 660 | const char_t* get() const; 661 | 662 | // Get text, or the default value if object is empty 663 | const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; 664 | 665 | // Get text as a number, or the default value if conversion did not succeed or object is empty 666 | int as_int(int def = 0) const; 667 | unsigned int as_uint(unsigned int def = 0) const; 668 | double as_double(double def = 0) const; 669 | float as_float(float def = 0) const; 670 | 671 | #ifdef PUGIXML_HAS_LONG_LONG 672 | long long as_llong(long long def = 0) const; 673 | unsigned long long as_ullong(unsigned long long def = 0) const; 674 | #endif 675 | 676 | // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty 677 | bool as_bool(bool def = false) const; 678 | 679 | // Set text (returns false if object is empty or there is not enough memory) 680 | bool set(const char_t* rhs); 681 | 682 | // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") 683 | bool set(int rhs); 684 | bool set(unsigned int rhs); 685 | bool set(double rhs); 686 | bool set(bool rhs); 687 | 688 | #ifdef PUGIXML_HAS_LONG_LONG 689 | bool set(long long rhs); 690 | bool set(unsigned long long rhs); 691 | #endif 692 | 693 | // Set text (equivalent to set without error checking) 694 | xml_text& operator=(const char_t* rhs); 695 | xml_text& operator=(int rhs); 696 | xml_text& operator=(unsigned int rhs); 697 | xml_text& operator=(double rhs); 698 | xml_text& operator=(bool rhs); 699 | 700 | #ifdef PUGIXML_HAS_LONG_LONG 701 | xml_text& operator=(long long rhs); 702 | xml_text& operator=(unsigned long long rhs); 703 | #endif 704 | 705 | // Get the data node (node_pcdata or node_cdata) for this object 706 | xml_node data() const; 707 | }; 708 | 709 | #ifdef __BORLANDC__ 710 | // Borland C++ workaround 711 | bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); 712 | bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); 713 | #endif 714 | 715 | // Child node iterator (a bidirectional iterator over a collection of xml_node) 716 | class PUGIXML_CLASS xml_node_iterator 717 | { 718 | friend class xml_node; 719 | 720 | private: 721 | mutable xml_node _wrap; 722 | xml_node _parent; 723 | 724 | xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); 725 | 726 | public: 727 | // Iterator traits 728 | typedef ptrdiff_t difference_type; 729 | typedef xml_node value_type; 730 | typedef xml_node* pointer; 731 | typedef xml_node& reference; 732 | 733 | #ifndef PUGIXML_NO_STL 734 | typedef std::bidirectional_iterator_tag iterator_category; 735 | #endif 736 | 737 | // Default constructor 738 | xml_node_iterator(); 739 | 740 | // Construct an iterator which points to the specified node 741 | xml_node_iterator(const xml_node& node); 742 | 743 | // Iterator operators 744 | bool operator==(const xml_node_iterator& rhs) const; 745 | bool operator!=(const xml_node_iterator& rhs) const; 746 | 747 | xml_node& operator*() const; 748 | xml_node* operator->() const; 749 | 750 | const xml_node_iterator& operator++(); 751 | xml_node_iterator operator++(int); 752 | 753 | const xml_node_iterator& operator--(); 754 | xml_node_iterator operator--(int); 755 | }; 756 | 757 | // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) 758 | class PUGIXML_CLASS xml_attribute_iterator 759 | { 760 | friend class xml_node; 761 | 762 | private: 763 | mutable xml_attribute _wrap; 764 | xml_node _parent; 765 | 766 | xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); 767 | 768 | public: 769 | // Iterator traits 770 | typedef ptrdiff_t difference_type; 771 | typedef xml_attribute value_type; 772 | typedef xml_attribute* pointer; 773 | typedef xml_attribute& reference; 774 | 775 | #ifndef PUGIXML_NO_STL 776 | typedef std::bidirectional_iterator_tag iterator_category; 777 | #endif 778 | 779 | // Default constructor 780 | xml_attribute_iterator(); 781 | 782 | // Construct an iterator which points to the specified attribute 783 | xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); 784 | 785 | // Iterator operators 786 | bool operator==(const xml_attribute_iterator& rhs) const; 787 | bool operator!=(const xml_attribute_iterator& rhs) const; 788 | 789 | xml_attribute& operator*() const; 790 | xml_attribute* operator->() const; 791 | 792 | const xml_attribute_iterator& operator++(); 793 | xml_attribute_iterator operator++(int); 794 | 795 | const xml_attribute_iterator& operator--(); 796 | xml_attribute_iterator operator--(int); 797 | }; 798 | 799 | // Named node range helper 800 | class PUGIXML_CLASS xml_named_node_iterator 801 | { 802 | friend class xml_node; 803 | 804 | public: 805 | // Iterator traits 806 | typedef ptrdiff_t difference_type; 807 | typedef xml_node value_type; 808 | typedef xml_node* pointer; 809 | typedef xml_node& reference; 810 | 811 | #ifndef PUGIXML_NO_STL 812 | typedef std::bidirectional_iterator_tag iterator_category; 813 | #endif 814 | 815 | // Default constructor 816 | xml_named_node_iterator(); 817 | 818 | // Construct an iterator which points to the specified node 819 | xml_named_node_iterator(const xml_node& node, const char_t* name); 820 | 821 | // Iterator operators 822 | bool operator==(const xml_named_node_iterator& rhs) const; 823 | bool operator!=(const xml_named_node_iterator& rhs) const; 824 | 825 | xml_node& operator*() const; 826 | xml_node* operator->() const; 827 | 828 | const xml_named_node_iterator& operator++(); 829 | xml_named_node_iterator operator++(int); 830 | 831 | const xml_named_node_iterator& operator--(); 832 | xml_named_node_iterator operator--(int); 833 | 834 | private: 835 | mutable xml_node _wrap; 836 | xml_node _parent; 837 | const char_t* _name; 838 | 839 | xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); 840 | }; 841 | 842 | // Abstract tree walker class (see xml_node::traverse) 843 | class PUGIXML_CLASS xml_tree_walker 844 | { 845 | friend class xml_node; 846 | 847 | private: 848 | int _depth; 849 | 850 | protected: 851 | // Get current traversal depth 852 | int depth() const; 853 | 854 | public: 855 | xml_tree_walker(); 856 | virtual ~xml_tree_walker(); 857 | 858 | // Callback that is called when traversal begins 859 | virtual bool begin(xml_node& node); 860 | 861 | // Callback that is called for each node traversed 862 | virtual bool for_each(xml_node& node) = 0; 863 | 864 | // Callback that is called when traversal ends 865 | virtual bool end(xml_node& node); 866 | }; 867 | 868 | // Parsing status, returned as part of xml_parse_result object 869 | enum xml_parse_status 870 | { 871 | status_ok = 0, // No error 872 | 873 | status_file_not_found, // File was not found during load_file() 874 | status_io_error, // Error reading from file/stream 875 | status_out_of_memory, // Could not allocate memory 876 | status_internal_error, // Internal error occurred 877 | 878 | status_unrecognized_tag, // Parser could not determine tag type 879 | 880 | status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction 881 | status_bad_comment, // Parsing error occurred while parsing comment 882 | status_bad_cdata, // Parsing error occurred while parsing CDATA section 883 | status_bad_doctype, // Parsing error occurred while parsing document type declaration 884 | status_bad_pcdata, // Parsing error occurred while parsing PCDATA section 885 | status_bad_start_element, // Parsing error occurred while parsing start element tag 886 | status_bad_attribute, // Parsing error occurred while parsing element attribute 887 | status_bad_end_element, // Parsing error occurred while parsing end element tag 888 | status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) 889 | 890 | status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) 891 | 892 | status_no_document_element // Parsing resulted in a document without element nodes 893 | }; 894 | 895 | // Parsing result 896 | struct PUGIXML_CLASS xml_parse_result 897 | { 898 | // Parsing status (see xml_parse_status) 899 | xml_parse_status status; 900 | 901 | // Last parsed offset (in char_t units from start of input data) 902 | ptrdiff_t offset; 903 | 904 | // Source document encoding 905 | xml_encoding encoding; 906 | 907 | // Default constructor, initializes object to failed state 908 | xml_parse_result(); 909 | 910 | // Cast to bool operator 911 | operator bool() const; 912 | 913 | // Get error description 914 | const char* description() const; 915 | }; 916 | 917 | // Document class (DOM tree root) 918 | class PUGIXML_CLASS xml_document: public xml_node 919 | { 920 | private: 921 | char_t* _buffer; 922 | 923 | char _memory[192]; 924 | 925 | // Non-copyable semantics 926 | xml_document(const xml_document&); 927 | const xml_document& operator=(const xml_document&); 928 | 929 | void create(); 930 | void destroy(); 931 | 932 | public: 933 | // Default constructor, makes empty document 934 | xml_document(); 935 | 936 | // Destructor, invalidates all node/attribute handles to this document 937 | ~xml_document(); 938 | 939 | // Removes all nodes, leaving the empty document 940 | void reset(); 941 | 942 | // Removes all nodes, then copies the entire contents of the specified document 943 | void reset(const xml_document& proto); 944 | 945 | #ifndef PUGIXML_NO_STL 946 | // Load document from stream. 947 | xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 948 | xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); 949 | #endif 950 | 951 | // Load document from zero-terminated string. No encoding conversions are applied. 952 | xml_parse_result load(const char_t* contents, unsigned int options = parse_default); 953 | 954 | // Load document from file 955 | xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 956 | xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 957 | 958 | // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. 959 | xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 960 | 961 | // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). 962 | // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. 963 | xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 964 | 965 | // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). 966 | // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). 967 | xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); 968 | 969 | // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). 970 | void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 971 | 972 | #ifndef PUGIXML_NO_STL 973 | // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). 974 | void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 975 | void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; 976 | #endif 977 | 978 | // Save XML to file 979 | bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 980 | bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; 981 | 982 | // Get document element 983 | xml_node document_element() const; 984 | }; 985 | 986 | #ifndef PUGIXML_NO_XPATH 987 | // XPath query return type 988 | enum xpath_value_type 989 | { 990 | xpath_type_none, // Unknown type (query failed to compile) 991 | xpath_type_node_set, // Node set (xpath_node_set) 992 | xpath_type_number, // Number 993 | xpath_type_string, // String 994 | xpath_type_boolean // Boolean 995 | }; 996 | 997 | // XPath parsing result 998 | struct PUGIXML_CLASS xpath_parse_result 999 | { 1000 | // Error message (0 if no error) 1001 | const char* error; 1002 | 1003 | // Last parsed offset (in char_t units from string start) 1004 | ptrdiff_t offset; 1005 | 1006 | // Default constructor, initializes object to failed state 1007 | xpath_parse_result(); 1008 | 1009 | // Cast to bool operator 1010 | operator bool() const; 1011 | 1012 | // Get error description 1013 | const char* description() const; 1014 | }; 1015 | 1016 | // A single XPath variable 1017 | class PUGIXML_CLASS xpath_variable 1018 | { 1019 | friend class xpath_variable_set; 1020 | 1021 | protected: 1022 | xpath_value_type _type; 1023 | xpath_variable* _next; 1024 | 1025 | xpath_variable(); 1026 | 1027 | // Non-copyable semantics 1028 | xpath_variable(const xpath_variable&); 1029 | xpath_variable& operator=(const xpath_variable&); 1030 | 1031 | public: 1032 | // Get variable name 1033 | const char_t* name() const; 1034 | 1035 | // Get variable type 1036 | xpath_value_type type() const; 1037 | 1038 | // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error 1039 | bool get_boolean() const; 1040 | double get_number() const; 1041 | const char_t* get_string() const; 1042 | const xpath_node_set& get_node_set() const; 1043 | 1044 | // Set variable value; no type conversion is performed, false is returned on type mismatch error 1045 | bool set(bool value); 1046 | bool set(double value); 1047 | bool set(const char_t* value); 1048 | bool set(const xpath_node_set& value); 1049 | }; 1050 | 1051 | // A set of XPath variables 1052 | class PUGIXML_CLASS xpath_variable_set 1053 | { 1054 | private: 1055 | xpath_variable* _data[64]; 1056 | 1057 | // Non-copyable semantics 1058 | xpath_variable_set(const xpath_variable_set&); 1059 | xpath_variable_set& operator=(const xpath_variable_set&); 1060 | 1061 | xpath_variable* find(const char_t* name) const; 1062 | 1063 | public: 1064 | // Default constructor/destructor 1065 | xpath_variable_set(); 1066 | ~xpath_variable_set(); 1067 | 1068 | // Add a new variable or get the existing one, if the types match 1069 | xpath_variable* add(const char_t* name, xpath_value_type type); 1070 | 1071 | // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch 1072 | bool set(const char_t* name, bool value); 1073 | bool set(const char_t* name, double value); 1074 | bool set(const char_t* name, const char_t* value); 1075 | bool set(const char_t* name, const xpath_node_set& value); 1076 | 1077 | // Get existing variable by name 1078 | xpath_variable* get(const char_t* name); 1079 | const xpath_variable* get(const char_t* name) const; 1080 | }; 1081 | 1082 | // A compiled XPath query object 1083 | class PUGIXML_CLASS xpath_query 1084 | { 1085 | private: 1086 | void* _impl; 1087 | xpath_parse_result _result; 1088 | 1089 | typedef void (*unspecified_bool_type)(xpath_query***); 1090 | 1091 | // Non-copyable semantics 1092 | xpath_query(const xpath_query&); 1093 | xpath_query& operator=(const xpath_query&); 1094 | 1095 | public: 1096 | // Construct a compiled object from XPath expression. 1097 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. 1098 | explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); 1099 | 1100 | // Destructor 1101 | ~xpath_query(); 1102 | 1103 | // Get query expression return type 1104 | xpath_value_type return_type() const; 1105 | 1106 | // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. 1107 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1108 | bool evaluate_boolean(const xpath_node& n) const; 1109 | 1110 | // Evaluate expression as double value in the specified context; performs type conversion if necessary. 1111 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1112 | double evaluate_number(const xpath_node& n) const; 1113 | 1114 | #ifndef PUGIXML_NO_STL 1115 | // Evaluate expression as string value in the specified context; performs type conversion if necessary. 1116 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1117 | string_t evaluate_string(const xpath_node& n) const; 1118 | #endif 1119 | 1120 | // Evaluate expression as string value in the specified context; performs type conversion if necessary. 1121 | // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). 1122 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. 1123 | // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. 1124 | size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; 1125 | 1126 | // Evaluate expression as node set in the specified context. 1127 | // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. 1128 | // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. 1129 | xpath_node_set evaluate_node_set(const xpath_node& n) const; 1130 | 1131 | // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) 1132 | const xpath_parse_result& result() const; 1133 | 1134 | // Safe bool conversion operator 1135 | operator unspecified_bool_type() const; 1136 | 1137 | // Borland C++ workaround 1138 | bool operator!() const; 1139 | }; 1140 | 1141 | #ifndef PUGIXML_NO_EXCEPTIONS 1142 | // XPath exception class 1143 | class PUGIXML_CLASS xpath_exception: public std::exception 1144 | { 1145 | private: 1146 | xpath_parse_result _result; 1147 | 1148 | public: 1149 | // Construct exception from parse result 1150 | explicit xpath_exception(const xpath_parse_result& result); 1151 | 1152 | // Get error message 1153 | virtual const char* what() const throw(); 1154 | 1155 | // Get parse result 1156 | const xpath_parse_result& result() const; 1157 | }; 1158 | #endif 1159 | 1160 | // XPath node class (either xml_node or xml_attribute) 1161 | class PUGIXML_CLASS xpath_node 1162 | { 1163 | private: 1164 | xml_node _node; 1165 | xml_attribute _attribute; 1166 | 1167 | typedef void (*unspecified_bool_type)(xpath_node***); 1168 | 1169 | public: 1170 | // Default constructor; constructs empty XPath node 1171 | xpath_node(); 1172 | 1173 | // Construct XPath node from XML node/attribute 1174 | xpath_node(const xml_node& node); 1175 | xpath_node(const xml_attribute& attribute, const xml_node& parent); 1176 | 1177 | // Get node/attribute, if any 1178 | xml_node node() const; 1179 | xml_attribute attribute() const; 1180 | 1181 | // Get parent of contained node/attribute 1182 | xml_node parent() const; 1183 | 1184 | // Safe bool conversion operator 1185 | operator unspecified_bool_type() const; 1186 | 1187 | // Borland C++ workaround 1188 | bool operator!() const; 1189 | 1190 | // Comparison operators 1191 | bool operator==(const xpath_node& n) const; 1192 | bool operator!=(const xpath_node& n) const; 1193 | }; 1194 | 1195 | #ifdef __BORLANDC__ 1196 | // Borland C++ workaround 1197 | bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); 1198 | bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); 1199 | #endif 1200 | 1201 | // A fixed-size collection of XPath nodes 1202 | class PUGIXML_CLASS xpath_node_set 1203 | { 1204 | public: 1205 | // Collection type 1206 | enum type_t 1207 | { 1208 | type_unsorted, // Not ordered 1209 | type_sorted, // Sorted by document order (ascending) 1210 | type_sorted_reverse // Sorted by document order (descending) 1211 | }; 1212 | 1213 | // Constant iterator type 1214 | typedef const xpath_node* const_iterator; 1215 | 1216 | // Default constructor. Constructs empty set. 1217 | xpath_node_set(); 1218 | 1219 | // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful 1220 | xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); 1221 | 1222 | // Destructor 1223 | ~xpath_node_set(); 1224 | 1225 | // Copy constructor/assignment operator 1226 | xpath_node_set(const xpath_node_set& ns); 1227 | xpath_node_set& operator=(const xpath_node_set& ns); 1228 | 1229 | // Get collection type 1230 | type_t type() const; 1231 | 1232 | // Get collection size 1233 | size_t size() const; 1234 | 1235 | // Indexing operator 1236 | const xpath_node& operator[](size_t index) const; 1237 | 1238 | // Collection iterators 1239 | const_iterator begin() const; 1240 | const_iterator end() const; 1241 | 1242 | // Sort the collection in ascending/descending order by document order 1243 | void sort(bool reverse = false); 1244 | 1245 | // Get first node in the collection by document order 1246 | xpath_node first() const; 1247 | 1248 | // Check if collection is empty 1249 | bool empty() const; 1250 | 1251 | private: 1252 | type_t _type; 1253 | 1254 | xpath_node _storage; 1255 | 1256 | xpath_node* _begin; 1257 | xpath_node* _end; 1258 | 1259 | void _assign(const_iterator begin, const_iterator end); 1260 | }; 1261 | #endif 1262 | 1263 | #ifndef PUGIXML_NO_STL 1264 | // Convert wide string to UTF8 1265 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); 1266 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); 1267 | 1268 | // Convert UTF8 to wide string 1269 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); 1270 | std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); 1271 | #endif 1272 | 1273 | // Memory allocation function interface; returns pointer to allocated memory or NULL on failure 1274 | typedef void* (*allocation_function)(size_t size); 1275 | 1276 | // Memory deallocation function interface 1277 | typedef void (*deallocation_function)(void* ptr); 1278 | 1279 | // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. 1280 | void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); 1281 | 1282 | // Get current memory management functions 1283 | allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); 1284 | deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); 1285 | } 1286 | 1287 | #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) 1288 | namespace std 1289 | { 1290 | // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) 1291 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); 1292 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); 1293 | std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); 1294 | } 1295 | #endif 1296 | 1297 | #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) 1298 | namespace std 1299 | { 1300 | // Workarounds for (non-standard) iterator category detection 1301 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); 1302 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); 1303 | std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); 1304 | } 1305 | #endif 1306 | 1307 | #endif 1308 | 1309 | /** 1310 | * Copyright (c) 2006-2014 Arseny Kapoulkine 1311 | * 1312 | * Permission is hereby granted, free of charge, to any person 1313 | * obtaining a copy of this software and associated documentation 1314 | * files (the "Software"), to deal in the Software without 1315 | * restriction, including without limitation the rights to use, 1316 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 1317 | * copies of the Software, and to permit persons to whom the 1318 | * Software is furnished to do so, subject to the following 1319 | * conditions: 1320 | * 1321 | * The above copyright notice and this permission notice shall be 1322 | * included in all copies or substantial portions of the Software. 1323 | * 1324 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1325 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 1326 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1327 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 1328 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 1329 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1330 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1331 | * OTHER DEALINGS IN THE SOFTWARE. 1332 | */ 1333 | --------------------------------------------------------------------------------