├── .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 | [](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 < / detection; use UTF8 if BOM is not found
175 | encoding_utf8, // UTF8 encoding
176 | encoding_utf16_le, // Little-endian UTF16
177 | encoding_utf16_be, // Big-endian UTF16
178 | encoding_utf16, // UTF16 with native endianness
179 | encoding_utf32_le, // Little-endian UTF32
180 | encoding_utf32_be, // Big-endian UTF32
181 | encoding_utf32, // UTF32 with native endianness
182 | encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)
183 | encoding_latin1
184 | };
185 |
186 | // Formatting flags
187 |
188 | // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
189 | const unsigned int format_indent = 0x01;
190 |
191 | // Write encoding-specific BOM to the output stream. This flag is off by default.
192 | const unsigned int format_write_bom = 0x02;
193 |
194 | // Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
195 | const unsigned int format_raw = 0x04;
196 |
197 | // Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
198 | const unsigned int format_no_declaration = 0x08;
199 |
200 | // Don't escape attribute values and PCDATA contents. This flag is off by default.
201 | const unsigned int format_no_escapes = 0x10;
202 |
203 | // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
204 | const unsigned int format_save_file_text = 0x20;
205 |
206 | // The default set of formatting flags.
207 | // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
208 | const unsigned int format_default = format_indent;
209 |
210 | // Forward declarations
211 | struct xml_attribute_struct;
212 | struct xml_node_struct;
213 |
214 | class xml_node_iterator;
215 | class xml_attribute_iterator;
216 | class xml_named_node_iterator;
217 |
218 | class xml_tree_walker;
219 |
220 | struct xml_parse_result;
221 |
222 | class xml_node;
223 |
224 | class xml_text;
225 |
226 | #ifndef PUGIXML_NO_XPATH
227 | class xpath_node;
228 | class xpath_node_set;
229 | class xpath_query;
230 | class xpath_variable_set;
231 | #endif
232 |
233 | // Range-based for loop support
234 | template 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 |
--------------------------------------------------------------------------------