/src/gdal/frmts/nitf/rpftocfile.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: RPF A.TOC read Library |
4 | | * Purpose: Module responsible for opening a RPF TOC file, populating RPFToc |
5 | | * structure |
6 | | * Author: Even Rouault, even.rouault at spatialys.com |
7 | | * |
8 | | ********************************************************************** |
9 | | * Copyright (c) 2007-2010, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | /* Portions of code are placed under the following copyright : */ |
15 | | /* |
16 | | ****************************************************************************** |
17 | | * Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc |
18 | | * Permission to use, copy, modify and distribute this software and |
19 | | * its documentation for any purpose and without fee is hereby granted, |
20 | | * provided that the above copyright notice appear in all copies, that |
21 | | * both the copyright notice and this permission notice appear in |
22 | | * supporting documentation, and that the name of L.A.S. Inc not be used |
23 | | * in advertising or publicity pertaining to distribution of the software |
24 | | * without specific, written prior permission. L.A.S. Inc. makes no |
25 | | * representations about the suitability of this software for any purpose. |
26 | | * It is provided "as is" without express or implied warranty. |
27 | | ****************************************************************************** |
28 | | */ |
29 | | |
30 | | #include "cpl_port.h" |
31 | | #include "rpftoclib.h" |
32 | | |
33 | | #include <climits> |
34 | | #include <cmath> |
35 | | #include <cstring> |
36 | | |
37 | | #include "cpl_conv.h" |
38 | | #include "cpl_error.h" |
39 | | #include "cpl_string.h" |
40 | | #include "cpl_vsi.h" |
41 | | #include "nitflib.h" |
42 | | |
43 | | /************************************************************************/ |
44 | | /* RPFTOCTrim() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | static void RPFTOCTrim(char *str) |
48 | 0 | { |
49 | 0 | char *c = str; |
50 | 0 | if (str == nullptr || *str == 0) |
51 | 0 | return; |
52 | | |
53 | 0 | while (*c == ' ') |
54 | 0 | { |
55 | 0 | c++; |
56 | 0 | } |
57 | 0 | if (c != str) |
58 | 0 | { |
59 | 0 | memmove(str, c, strlen(c) + 1); |
60 | 0 | } |
61 | |
|
62 | 0 | int i = static_cast<int>(strlen(str)) - 1; |
63 | 0 | while (i >= 0 && str[i] == ' ') |
64 | 0 | { |
65 | 0 | str[i] = 0; |
66 | 0 | i--; |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | /************************************************************************/ |
71 | | /* RPFTOCRead() */ |
72 | | /************************************************************************/ |
73 | | |
74 | | RPFToc *RPFTOCRead(const char *pszFilename, NITFFile *psFile) |
75 | 0 | { |
76 | 0 | int nTRESize; |
77 | 0 | const char *pachTRE = |
78 | 0 | NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "RPFHDR", &nTRESize); |
79 | 0 | if (pachTRE == nullptr) |
80 | 0 | { |
81 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
82 | 0 | "Invalid TOC file. Can't find RPFHDR."); |
83 | 0 | return nullptr; |
84 | 0 | } |
85 | | |
86 | 0 | if (nTRESize != 48) |
87 | 0 | { |
88 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "RPFHDR TRE wrong size."); |
89 | 0 | return nullptr; |
90 | 0 | } |
91 | | |
92 | 0 | return RPFTOCReadFromBuffer(pszFilename, psFile->fp, pachTRE); |
93 | 0 | } |
94 | | |
95 | | /* This function is directly inspired by function parse_toc coming from |
96 | | * ogdi/driver/rpf/utils.c */ |
97 | | |
98 | | RPFToc *RPFTOCReadFromBuffer(const char *pszFilename, VSILFILE *fp, |
99 | | const char *tocHeader) |
100 | 18 | { |
101 | 18 | tocHeader += 1; /* skip endian */ |
102 | 18 | tocHeader += 2; /* skip header length */ |
103 | 18 | tocHeader += 12; /* skip file name : this should be A.TOC (padded) */ |
104 | 18 | tocHeader += 1; /* skip new */ |
105 | 18 | tocHeader += 15; /* skip standard_num */ |
106 | 18 | tocHeader += 8; /* skip standard_date */ |
107 | 18 | tocHeader += 1; /* skip classification */ |
108 | 18 | tocHeader += 2; /* skip country */ |
109 | 18 | tocHeader += 2; /* skip release */ |
110 | | |
111 | 18 | unsigned int locationSectionPhysicalLocation; |
112 | 18 | memcpy(&locationSectionPhysicalLocation, tocHeader, sizeof(unsigned int)); |
113 | 18 | CPL_MSBPTR32(&locationSectionPhysicalLocation); |
114 | | |
115 | 18 | if (VSIFSeekL(fp, |
116 | 18 | static_cast<vsi_l_offset>(locationSectionPhysicalLocation), |
117 | 18 | SEEK_SET) != 0) |
118 | 0 | { |
119 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
120 | 0 | "Invalid TOC file. Unable to seek to " |
121 | 0 | "locationSectionPhysicalLocation at offset %d.", |
122 | 0 | locationSectionPhysicalLocation); |
123 | 0 | return nullptr; |
124 | 0 | } |
125 | | |
126 | 18 | int nSections; |
127 | 18 | NITFLocation *pasLocations = NITFReadRPFLocationTable(fp, &nSections); |
128 | | |
129 | 18 | unsigned int boundaryRectangleSectionSubHeaderPhysIndex = 0; |
130 | 18 | unsigned int boundaryRectangleTablePhysIndex = 0; |
131 | 18 | unsigned int frameFileIndexSectionSubHeaderPhysIndex = 0; |
132 | 18 | unsigned int frameFileIndexSubsectionPhysIndex = 0; |
133 | | |
134 | 18 | for (int i = 0; i < nSections; i++) |
135 | 0 | { |
136 | 0 | if (pasLocations[i].nLocId == LID_BoundaryRectangleSectionSubheader) |
137 | 0 | { |
138 | 0 | boundaryRectangleSectionSubHeaderPhysIndex = |
139 | 0 | pasLocations[i].nLocOffset; |
140 | 0 | } |
141 | 0 | else if (pasLocations[i].nLocId == LID_BoundaryRectangleTable) |
142 | 0 | { |
143 | 0 | boundaryRectangleTablePhysIndex = pasLocations[i].nLocOffset; |
144 | 0 | } |
145 | 0 | else if (pasLocations[i].nLocId == LID_FrameFileIndexSectionSubHeader) |
146 | 0 | { |
147 | 0 | frameFileIndexSectionSubHeaderPhysIndex = |
148 | 0 | pasLocations[i].nLocOffset; |
149 | 0 | } |
150 | 0 | else if (pasLocations[i].nLocId == LID_FrameFileIndexSubsection) |
151 | 0 | { |
152 | 0 | frameFileIndexSubsectionPhysIndex = pasLocations[i].nLocOffset; |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | 18 | CPLFree(pasLocations); |
157 | | |
158 | 18 | if (boundaryRectangleSectionSubHeaderPhysIndex == 0) |
159 | 18 | { |
160 | 18 | CPLError(CE_Failure, CPLE_NotSupported, |
161 | 18 | "Invalid TOC file. Can't find " |
162 | 18 | "LID_BoundaryRectangleSectionSubheader."); |
163 | 18 | return nullptr; |
164 | 18 | } |
165 | 0 | if (boundaryRectangleTablePhysIndex == 0) |
166 | 0 | { |
167 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
168 | 0 | "Invalid TOC file. Can't find LID_BoundaryRectangleTable."); |
169 | 0 | return nullptr; |
170 | 0 | } |
171 | 0 | if (frameFileIndexSectionSubHeaderPhysIndex == 0) |
172 | 0 | { |
173 | 0 | CPLError( |
174 | 0 | CE_Failure, CPLE_NotSupported, |
175 | 0 | "Invalid TOC file. Can't find LID_FrameFileIndexSectionSubHeader."); |
176 | 0 | return nullptr; |
177 | 0 | } |
178 | 0 | if (frameFileIndexSubsectionPhysIndex == 0) |
179 | 0 | { |
180 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
181 | 0 | "Invalid TOC file. Can't find LID_FrameFileIndexSubsection."); |
182 | 0 | return nullptr; |
183 | 0 | } |
184 | | |
185 | 0 | if (VSIFSeekL(fp, |
186 | 0 | static_cast<vsi_l_offset>( |
187 | 0 | boundaryRectangleSectionSubHeaderPhysIndex), |
188 | 0 | SEEK_SET) != 0) |
189 | 0 | { |
190 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
191 | 0 | "Invalid TOC file. Unable to seek to " |
192 | 0 | "boundaryRectangleSectionSubHeaderPhysIndex at offset %d.", |
193 | 0 | boundaryRectangleSectionSubHeaderPhysIndex); |
194 | 0 | return nullptr; |
195 | 0 | } |
196 | | |
197 | 0 | unsigned int boundaryRectangleTableOffset; |
198 | 0 | bool bOK = VSIFReadL(&boundaryRectangleTableOffset, |
199 | 0 | sizeof(boundaryRectangleTableOffset), 1, fp) == 1; |
200 | 0 | CPL_MSBPTR32(&boundaryRectangleTableOffset); |
201 | |
|
202 | 0 | unsigned short boundaryRectangleCount; |
203 | 0 | bOK &= VSIFReadL(&boundaryRectangleCount, sizeof(boundaryRectangleCount), 1, |
204 | 0 | fp) == 1; |
205 | 0 | CPL_MSBPTR16(&boundaryRectangleCount); |
206 | |
|
207 | 0 | if (!bOK || |
208 | 0 | VSIFSeekL(fp, |
209 | 0 | static_cast<vsi_l_offset>(boundaryRectangleTablePhysIndex), |
210 | 0 | SEEK_SET) != 0) |
211 | 0 | { |
212 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
213 | 0 | "Invalid TOC file. Unable to seek to " |
214 | 0 | "boundaryRectangleTablePhysIndex at offset %d.", |
215 | 0 | boundaryRectangleTablePhysIndex); |
216 | 0 | return nullptr; |
217 | 0 | } |
218 | | |
219 | 0 | RPFToc *toc = static_cast<RPFToc *>(CPLMalloc(sizeof(RPFToc))); |
220 | 0 | toc->nEntries = boundaryRectangleCount; |
221 | 0 | toc->entries = reinterpret_cast<RPFTocEntry *>( |
222 | 0 | CPLMalloc(boundaryRectangleCount * sizeof(RPFTocEntry))); |
223 | 0 | memset(toc->entries, 0, boundaryRectangleCount * sizeof(RPFTocEntry)); |
224 | |
|
225 | 0 | for (int i = 0; i < toc->nEntries; i++) |
226 | 0 | { |
227 | 0 | toc->entries[i].isOverviewOrLegend = 0; |
228 | |
|
229 | 0 | bOK &= VSIFReadL(toc->entries[i].type, 1, 5, fp) == 5; |
230 | 0 | toc->entries[i].type[5] = 0; |
231 | 0 | RPFTOCTrim(toc->entries[i].type); |
232 | |
|
233 | 0 | bOK &= VSIFReadL(toc->entries[i].compression, 1, 5, fp) == 5; |
234 | 0 | toc->entries[i].compression[5] = 0; |
235 | 0 | RPFTOCTrim(toc->entries[i].compression); |
236 | |
|
237 | 0 | bOK &= VSIFReadL(toc->entries[i].scale, 1, 12, fp) == 12; |
238 | 0 | toc->entries[i].scale[12] = 0; |
239 | 0 | RPFTOCTrim(toc->entries[i].scale); |
240 | 0 | if (toc->entries[i].scale[0] == '1' && toc->entries[i].scale[1] == ':') |
241 | 0 | { |
242 | 0 | memmove(toc->entries[i].scale, toc->entries[i].scale + 2, |
243 | 0 | strlen(toc->entries[i].scale + 2) + 1); |
244 | 0 | } |
245 | |
|
246 | 0 | bOK &= VSIFReadL(toc->entries[i].zone, 1, 1, fp) == 1; |
247 | 0 | toc->entries[i].zone[1] = 0; |
248 | 0 | RPFTOCTrim(toc->entries[i].zone); |
249 | |
|
250 | 0 | bOK &= VSIFReadL(toc->entries[i].producer, 1, 5, fp) == 5; |
251 | 0 | toc->entries[i].producer[5] = 0; |
252 | 0 | RPFTOCTrim(toc->entries[i].producer); |
253 | |
|
254 | 0 | bOK &= VSIFReadL(&toc->entries[i].nwLat, sizeof(double), 1, fp) == 1; |
255 | 0 | CPL_MSBPTR64(&toc->entries[i].nwLat); |
256 | |
|
257 | 0 | bOK &= VSIFReadL(&toc->entries[i].nwLong, sizeof(double), 1, fp) == 1; |
258 | 0 | CPL_MSBPTR64(&toc->entries[i].nwLong); |
259 | |
|
260 | 0 | bOK &= VSIFReadL(&toc->entries[i].swLat, sizeof(double), 1, fp) == 1; |
261 | 0 | CPL_MSBPTR64(&toc->entries[i].swLat); |
262 | |
|
263 | 0 | bOK &= VSIFReadL(&toc->entries[i].swLong, sizeof(double), 1, fp) == 1; |
264 | 0 | CPL_MSBPTR64(&toc->entries[i].swLong); |
265 | |
|
266 | 0 | bOK &= VSIFReadL(&toc->entries[i].neLat, sizeof(double), 1, fp) == 1; |
267 | 0 | CPL_MSBPTR64(&toc->entries[i].neLat); |
268 | |
|
269 | 0 | bOK &= VSIFReadL(&toc->entries[i].neLong, sizeof(double), 1, fp) == 1; |
270 | 0 | CPL_MSBPTR64(&toc->entries[i].neLong); |
271 | |
|
272 | 0 | bOK &= VSIFReadL(&toc->entries[i].seLat, sizeof(double), 1, fp) == 1; |
273 | 0 | CPL_MSBPTR64(&toc->entries[i].seLat); |
274 | |
|
275 | 0 | bOK &= VSIFReadL(&toc->entries[i].seLong, sizeof(double), 1, fp) == 1; |
276 | 0 | CPL_MSBPTR64(&toc->entries[i].seLong); |
277 | |
|
278 | 0 | bOK &= VSIFReadL(&toc->entries[i].vertResolution, sizeof(double), 1, |
279 | 0 | fp) == 1; |
280 | 0 | CPL_MSBPTR64(&toc->entries[i].vertResolution); |
281 | |
|
282 | 0 | bOK &= VSIFReadL(&toc->entries[i].horizResolution, sizeof(double), 1, |
283 | 0 | fp) == 1; |
284 | 0 | CPL_MSBPTR64(&toc->entries[i].horizResolution); |
285 | |
|
286 | 0 | bOK &= VSIFReadL(&toc->entries[i].vertInterval, sizeof(double), 1, |
287 | 0 | fp) == 1; |
288 | 0 | CPL_MSBPTR64(&toc->entries[i].vertInterval); |
289 | |
|
290 | 0 | bOK &= VSIFReadL(&toc->entries[i].horizInterval, sizeof(double), 1, |
291 | 0 | fp) == 1; |
292 | 0 | CPL_MSBPTR64(&toc->entries[i].horizInterval); |
293 | |
|
294 | 0 | bOK &= VSIFReadL(&toc->entries[i].nVertFrames, sizeof(int), 1, fp) == 1; |
295 | 0 | CPL_MSBPTR32(&toc->entries[i].nVertFrames); |
296 | |
|
297 | 0 | bOK &= |
298 | 0 | VSIFReadL(&toc->entries[i].nHorizFrames, sizeof(int), 1, fp) == 1; |
299 | 0 | CPL_MSBPTR32(&toc->entries[i].nHorizFrames); |
300 | |
|
301 | 0 | if (!bOK) |
302 | 0 | { |
303 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
304 | 0 | toc->entries[i].nVertFrames = 0; |
305 | 0 | toc->entries[i].nHorizFrames = 0; |
306 | 0 | RPFTOCFree(toc); |
307 | 0 | return nullptr; |
308 | 0 | } |
309 | | |
310 | | // do some basic plausibility checks for all entries |
311 | 0 | if (toc->entries[i].vertInterval <= 1e-10 || |
312 | 0 | !std::isfinite(toc->entries[i].vertInterval) || |
313 | 0 | toc->entries[i].horizInterval <= 1e-10 || |
314 | 0 | !std::isfinite(toc->entries[i].horizInterval) || |
315 | 0 | toc->entries[i].nHorizFrames == 0 || |
316 | 0 | toc->entries[i].nVertFrames == 0 || |
317 | 0 | toc->entries[i].nHorizFrames > |
318 | 0 | INT_MAX / toc->entries[i].nVertFrames) |
319 | 0 | { |
320 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry"); |
321 | 0 | toc->entries[i].nVertFrames = 0; |
322 | 0 | toc->entries[i].nHorizFrames = 0; |
323 | 0 | RPFTOCFree(toc); |
324 | 0 | return nullptr; |
325 | 0 | } |
326 | | |
327 | | // Overview has ZONE 'R' and Legend ZONE 'D' but because the Zone 'D' is |
328 | | // also a valid Zone we need an additional check. -> In all cases of |
329 | | // Overview/Legend the values of the BoundingBox are equal so we simply |
330 | | // check here that NW == SE is. |
331 | 0 | toc->entries[i].isOverviewOrLegend = |
332 | 0 | (toc->entries[i].zone[0] == 'R' || // Overview |
333 | 0 | (toc->entries[i].zone[0] == 'D' && // Legend |
334 | 0 | memcmp(&(toc->entries[i].seLong), &(toc->entries[i].nwLong), |
335 | 0 | sizeof(toc->entries[i].nwLong)) == 0 && |
336 | 0 | memcmp(&(toc->entries[i].seLat), &(toc->entries[i].nwLat), |
337 | 0 | sizeof(toc->entries[i].nwLat)) == 0)); |
338 | |
|
339 | 0 | bool isPolarZone = (toc->entries[i].zone[0] == '9') || |
340 | 0 | (toc->entries[i].zone[0] == 'J'); |
341 | | |
342 | | // make additional checks of the bounding for charts (without Legends |
343 | | // and Overviews) |
344 | 0 | if (!toc->entries[i].isOverviewOrLegend) |
345 | 0 | { |
346 | 0 | if (!(fabs(toc->entries[i].seLong) <= 360.0) || |
347 | 0 | !(fabs(toc->entries[i].nwLong) <= 360.0) || |
348 | 0 | !(fabs(toc->entries[i].nwLat) <= 90.0) || |
349 | 0 | !(fabs(toc->entries[i].seLat) <= 90.0) || |
350 | | // check only for non-polar zones, because the values are not |
351 | | // always correct here |
352 | 0 | (!isPolarZone && |
353 | 0 | (toc->entries[i].seLong < toc->entries[i].nwLong || |
354 | 0 | toc->entries[i].nwLat < toc->entries[i].seLat))) |
355 | 0 | { |
356 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry"); |
357 | 0 | toc->entries[i].nVertFrames = 0; |
358 | 0 | toc->entries[i].nHorizFrames = 0; |
359 | 0 | RPFTOCFree(toc); |
360 | 0 | return nullptr; |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | | // TODO: We could probably use another data structure, like a list, |
365 | | // instead of an array referenced by the frame coordinate... |
366 | 0 | if (static_cast<int>(toc->entries[i].nHorizFrames * |
367 | 0 | toc->entries[i].nVertFrames) > |
368 | 0 | atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000"))) |
369 | 0 | { |
370 | 0 | CPLError( |
371 | 0 | CE_Failure, CPLE_AppDefined, |
372 | 0 | "nHorizFrames=%d x nVertFrames=%d > %d. Please raise " |
373 | 0 | "the value of the RPFTOC_MAX_FRAME_COUNT configuration " |
374 | 0 | "option to more than %d if this dataset is legitimate.", |
375 | 0 | toc->entries[i].nHorizFrames, toc->entries[i].nVertFrames, |
376 | 0 | atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")), |
377 | 0 | toc->entries[i].nHorizFrames * toc->entries[i].nVertFrames); |
378 | 0 | toc->entries[i].frameEntries = nullptr; |
379 | 0 | } |
380 | 0 | else |
381 | 0 | { |
382 | 0 | toc->entries[i].frameEntries = |
383 | 0 | static_cast<RPFTocFrameEntry *>(VSI_CALLOC_VERBOSE( |
384 | 0 | static_cast<size_t>(toc->entries[i].nVertFrames) * |
385 | 0 | toc->entries[i].nHorizFrames, |
386 | 0 | sizeof(RPFTocFrameEntry))); |
387 | 0 | } |
388 | 0 | if (toc->entries[i].frameEntries == nullptr) |
389 | 0 | { |
390 | 0 | toc->entries[i].nVertFrames = 0; |
391 | 0 | toc->entries[i].nHorizFrames = 0; |
392 | 0 | RPFTOCFree(toc); |
393 | 0 | return nullptr; |
394 | 0 | } |
395 | | |
396 | 0 | CPLDebug("RPFTOC", |
397 | 0 | "[%d] type=%s, compression=%s, scale=%s, zone=%s, " |
398 | 0 | "producer=%s, nVertFrames=%d, nHorizFrames=%d", |
399 | 0 | i, toc->entries[i].type, toc->entries[i].compression, |
400 | 0 | toc->entries[i].scale, toc->entries[i].zone, |
401 | 0 | toc->entries[i].producer, toc->entries[i].nVertFrames, |
402 | 0 | toc->entries[i].nHorizFrames); |
403 | 0 | } |
404 | | |
405 | 0 | if (VSIFSeekL( |
406 | 0 | fp, |
407 | 0 | static_cast<vsi_l_offset>(frameFileIndexSectionSubHeaderPhysIndex), |
408 | 0 | SEEK_SET) != 0) |
409 | 0 | { |
410 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
411 | 0 | "Invalid TOC file. Unable to seek to " |
412 | 0 | "frameFileIndexSectionSubHeaderPhysIndex at offset %d.", |
413 | 0 | frameFileIndexSectionSubHeaderPhysIndex); |
414 | 0 | RPFTOCFree(toc); |
415 | 0 | return nullptr; |
416 | 0 | } |
417 | | |
418 | | /* Skip 1 byte security classification */ |
419 | 0 | bOK &= VSIFSeekL(fp, 1, SEEK_CUR) == 0; |
420 | |
|
421 | 0 | unsigned int frameIndexTableOffset; |
422 | 0 | bOK &= VSIFReadL(&frameIndexTableOffset, sizeof(frameIndexTableOffset), 1, |
423 | 0 | fp) == 1; |
424 | 0 | CPL_MSBPTR32(&frameIndexTableOffset); |
425 | |
|
426 | 0 | unsigned int nFrameFileIndexRecords; |
427 | 0 | bOK &= VSIFReadL(&nFrameFileIndexRecords, sizeof(nFrameFileIndexRecords), 1, |
428 | 0 | fp) == 1; |
429 | 0 | CPL_MSBPTR32(&nFrameFileIndexRecords); |
430 | |
|
431 | 0 | unsigned short nFrameFilePathnameRecords; |
432 | 0 | bOK &= VSIFReadL(&nFrameFilePathnameRecords, |
433 | 0 | sizeof(nFrameFilePathnameRecords), 1, fp) == 1; |
434 | 0 | CPL_MSBPTR16(&nFrameFilePathnameRecords); |
435 | |
|
436 | 0 | unsigned short frameFileIndexRecordLength; |
437 | 0 | bOK &= VSIFReadL(&frameFileIndexRecordLength, |
438 | 0 | sizeof(frameFileIndexRecordLength), 1, fp) == 1; |
439 | 0 | CPL_MSBPTR16(&frameFileIndexRecordLength); |
440 | 0 | if (frameFileIndexRecordLength < 3 * sizeof(short)) |
441 | 0 | { |
442 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Invalid file"); |
443 | 0 | RPFTOCFree(toc); |
444 | 0 | return nullptr; |
445 | 0 | } |
446 | | |
447 | 0 | if (!bOK) |
448 | 0 | { |
449 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
450 | 0 | RPFTOCFree(toc); |
451 | 0 | return nullptr; |
452 | 0 | } |
453 | | |
454 | 0 | int newBoundaryId = 0; |
455 | |
|
456 | 0 | for (int i = 0; i < static_cast<int>(nFrameFileIndexRecords); i++) |
457 | 0 | { |
458 | 0 | vsi_l_offset nFrameOffset = |
459 | 0 | static_cast<vsi_l_offset>(frameFileIndexSubsectionPhysIndex) + |
460 | 0 | static_cast<vsi_l_offset>(frameFileIndexRecordLength) * i; |
461 | 0 | if (VSIFSeekL(fp, nFrameOffset, SEEK_SET) != 0) |
462 | 0 | { |
463 | 0 | CPLError( |
464 | 0 | CE_Failure, CPLE_NotSupported, |
465 | 0 | "Invalid TOC file. Unable to seek to " |
466 | 0 | "frameFileIndexSubsectionPhysIndex(%d) at offset " CPL_FRMT_GUIB |
467 | 0 | ".", |
468 | 0 | i, static_cast<GUIntBig>(nFrameOffset)); |
469 | 0 | RPFTOCFree(toc); |
470 | 0 | return nullptr; |
471 | 0 | } |
472 | | |
473 | 0 | unsigned short boundaryId; |
474 | 0 | if (VSIFReadL(&boundaryId, sizeof(boundaryId), 1, fp) != 1) |
475 | 0 | { |
476 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
477 | 0 | RPFTOCFree(toc); |
478 | 0 | return nullptr; |
479 | 0 | } |
480 | 0 | CPL_MSBPTR16(&boundaryId); |
481 | |
|
482 | 0 | if (i == 0 && boundaryId == 0) |
483 | 0 | newBoundaryId = 1; |
484 | 0 | if (newBoundaryId == 0) |
485 | 0 | boundaryId--; |
486 | |
|
487 | 0 | if (boundaryId >= toc->nEntries) |
488 | 0 | { |
489 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
490 | 0 | "Invalid TOC file. Bad boundary id (%d) for frame file " |
491 | 0 | "index %d.", |
492 | 0 | boundaryId, i); |
493 | 0 | RPFTOCFree(toc); |
494 | 0 | return nullptr; |
495 | 0 | } |
496 | | |
497 | 0 | RPFTocEntry *entry = &toc->entries[boundaryId]; |
498 | 0 | entry->boundaryId = boundaryId; |
499 | |
|
500 | 0 | unsigned short frameRow; |
501 | 0 | bOK &= VSIFReadL(&frameRow, sizeof(frameRow), 1, fp) == 1; |
502 | 0 | CPL_MSBPTR16(&frameRow); |
503 | |
|
504 | 0 | unsigned short frameCol; |
505 | 0 | bOK &= VSIFReadL(&frameCol, sizeof(frameCol), 1, fp) == 1; |
506 | 0 | CPL_MSBPTR16(&frameCol); |
507 | 0 | if (!bOK) |
508 | 0 | { |
509 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
510 | 0 | RPFTOCFree(toc); |
511 | 0 | return nullptr; |
512 | 0 | } |
513 | | |
514 | 0 | if (newBoundaryId == 0) |
515 | 0 | { |
516 | 0 | frameRow--; |
517 | 0 | frameCol--; |
518 | 0 | } |
519 | 0 | else |
520 | 0 | { |
521 | | /* Trick so that frames are numbered north to south */ |
522 | 0 | if (entry->nVertFrames - 1 < frameRow) |
523 | 0 | { |
524 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
525 | 0 | "Invalid nVertFrames vs frameRow"); |
526 | 0 | RPFTOCFree(toc); |
527 | 0 | return nullptr; |
528 | 0 | } |
529 | 0 | frameRow = static_cast<unsigned short>((entry->nVertFrames - 1) - |
530 | 0 | frameRow); |
531 | 0 | } |
532 | | |
533 | 0 | if (frameRow >= entry->nVertFrames) |
534 | 0 | { |
535 | 0 | CPLError( |
536 | 0 | CE_Failure, CPLE_NotSupported, |
537 | 0 | "Invalid TOC file. Bad row num (%d) for frame file index %d.", |
538 | 0 | frameRow, i); |
539 | 0 | RPFTOCFree(toc); |
540 | 0 | return nullptr; |
541 | 0 | } |
542 | | |
543 | 0 | if (frameCol >= entry->nHorizFrames) |
544 | 0 | { |
545 | 0 | CPLError( |
546 | 0 | CE_Failure, CPLE_NotSupported, |
547 | 0 | "Invalid TOC file. Bad col num (%d) for frame file index %d.", |
548 | 0 | frameCol, i); |
549 | 0 | RPFTOCFree(toc); |
550 | 0 | return nullptr; |
551 | 0 | } |
552 | | |
553 | 0 | RPFTocFrameEntry *frameEntry = |
554 | 0 | &entry->frameEntries[frameRow * entry->nHorizFrames + frameCol]; |
555 | 0 | frameEntry->frameRow = frameRow; |
556 | 0 | frameEntry->frameCol = frameCol; |
557 | |
|
558 | 0 | if (frameEntry->exists) |
559 | 0 | { |
560 | 0 | CPLError( |
561 | 0 | CE_Warning, CPLE_AppDefined, |
562 | 0 | "Frame entry(%d,%d) for frame file index %d was already found.", |
563 | 0 | frameRow, frameCol, i); |
564 | 0 | CPLFree(frameEntry->directory); |
565 | 0 | frameEntry->directory = nullptr; |
566 | 0 | CPLFree(frameEntry->fullFilePath); |
567 | 0 | frameEntry->fullFilePath = nullptr; |
568 | 0 | frameEntry->exists = 0; |
569 | 0 | } |
570 | |
|
571 | 0 | unsigned int offsetFrameFilePathName; |
572 | 0 | bOK &= VSIFReadL(&offsetFrameFilePathName, |
573 | 0 | sizeof(offsetFrameFilePathName), 1, fp) == 1; |
574 | 0 | CPL_MSBPTR32(&offsetFrameFilePathName); |
575 | |
|
576 | 0 | bOK &= VSIFReadL(frameEntry->filename, 1, 12, fp) == 12; |
577 | 0 | if (!bOK) |
578 | 0 | { |
579 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
580 | 0 | RPFTOCFree(toc); |
581 | 0 | return nullptr; |
582 | 0 | } |
583 | 0 | frameEntry->filename[12] = '\0'; |
584 | 0 | bOK &= strlen(frameEntry->filename) > 0; |
585 | |
|
586 | 0 | if (CPLHasPathTraversal(frameEntry->filename)) |
587 | 0 | { |
588 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
589 | 0 | "Path traversal detected in %s", frameEntry->filename); |
590 | 0 | RPFTOCFree(toc); |
591 | 0 | return nullptr; |
592 | 0 | } |
593 | | |
594 | | // Check (case insensitive) if the filename is an overview or legend |
595 | | // some CADRG maps have legend name smaller than 8.3 then the extension |
596 | | // has blanks (0x20) at the end -> check only the first 3 letters of the |
597 | | // extension. |
598 | 0 | const std::string fileExt = CPLGetExtensionSafe(frameEntry->filename); |
599 | 0 | if (EQUALN(fileExt.c_str(), "ovr", 3) || |
600 | 0 | EQUALN(fileExt.c_str(), "lgd", 3)) |
601 | 0 | { |
602 | 0 | entry->isOverviewOrLegend = TRUE; |
603 | 0 | } |
604 | | |
605 | | /* Extract series code */ |
606 | 0 | if (entry->seriesAbbreviation == nullptr) |
607 | 0 | { |
608 | 0 | const NITFSeries *series = NITFGetSeriesInfo(frameEntry->filename); |
609 | 0 | if (series) |
610 | 0 | { |
611 | 0 | entry->seriesAbbreviation = series->abbreviation; |
612 | 0 | entry->seriesName = series->name; |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | | /* Get file geo reference */ |
617 | 0 | bOK &= VSIFReadL(frameEntry->georef, 1, 6, fp) == 6; |
618 | 0 | frameEntry->georef[6] = '\0'; |
619 | | |
620 | | /* Go to start of pathname record */ |
621 | | /* New path_off offset from start of frame file index section of TOC?? |
622 | | */ |
623 | | /* Add pathoffset wrt frame file index table subsection (loc[3]) */ |
624 | 0 | if (!bOK || VSIFSeekL(fp, |
625 | 0 | static_cast<vsi_l_offset>( |
626 | 0 | frameFileIndexSubsectionPhysIndex) + |
627 | 0 | offsetFrameFilePathName, |
628 | 0 | SEEK_SET) != 0) |
629 | 0 | { |
630 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
631 | 0 | "Invalid TOC file. Unable to seek to " |
632 | 0 | "frameFileIndexSubsectionPhysIndex + " |
633 | 0 | "offsetFrameFilePathName(%d) at offset " CPL_FRMT_GUIB ".", |
634 | 0 | i, |
635 | 0 | static_cast<GUIntBig>(frameFileIndexSubsectionPhysIndex) + |
636 | 0 | offsetFrameFilePathName); |
637 | 0 | RPFTOCFree(toc); |
638 | 0 | return nullptr; |
639 | 0 | } |
640 | | |
641 | 0 | unsigned short pathLength; |
642 | 0 | bOK &= VSIFReadL(&pathLength, sizeof(pathLength), 1, fp) == 1; |
643 | 0 | CPL_MSBPTR16(&pathLength); |
644 | | |
645 | | /* if nFrameFileIndexRecords == 65535 and pathLength == 65535 for each |
646 | | record, this leads to 4 GB allocation... Protect against this case */ |
647 | 0 | if (!bOK || pathLength == 0 || pathLength > 256) |
648 | 0 | { |
649 | 0 | CPLError( |
650 | 0 | CE_Failure, CPLE_NotSupported, |
651 | 0 | "Path length is invalid : %d. Probably corrupted TOC file.", |
652 | 0 | static_cast<int>(pathLength)); |
653 | 0 | RPFTOCFree(toc); |
654 | 0 | return nullptr; |
655 | 0 | } |
656 | | |
657 | 0 | frameEntry->directory = static_cast<char *>(CPLMalloc(pathLength + 1)); |
658 | 0 | bOK &= |
659 | 0 | VSIFReadL(frameEntry->directory, 1, pathLength, fp) == pathLength; |
660 | 0 | if (!bOK) |
661 | 0 | { |
662 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
663 | 0 | RPFTOCFree(toc); |
664 | 0 | return nullptr; |
665 | 0 | } |
666 | 0 | frameEntry->directory[pathLength] = 0; |
667 | 0 | if (frameEntry->directory[pathLength - 1] == '/') |
668 | 0 | frameEntry->directory[pathLength - 1] = 0; |
669 | |
|
670 | 0 | if (CPLHasPathTraversal(frameEntry->directory)) |
671 | 0 | { |
672 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
673 | 0 | "Path traversal detected in %s", frameEntry->directory); |
674 | 0 | RPFTOCFree(toc); |
675 | 0 | return nullptr; |
676 | 0 | } |
677 | | |
678 | 0 | if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == '/') |
679 | 0 | { |
680 | 0 | memmove(frameEntry->directory, frameEntry->directory + 2, |
681 | 0 | strlen(frameEntry->directory + 2) + 1); |
682 | | |
683 | | // Some A.TOC have subdirectory names like ".//X/" ... (#5979) |
684 | | // Check if it was not intended to be "./X/" instead. |
685 | 0 | VSIStatBufL sStatBuf; |
686 | 0 | if (frameEntry->directory[0] == '/' && |
687 | 0 | VSIStatL( |
688 | 0 | CPLFormFilenameSafe(CPLGetDirnameSafe(pszFilename).c_str(), |
689 | 0 | frameEntry->directory + 1, nullptr) |
690 | 0 | .c_str(), |
691 | 0 | &sStatBuf) == 0 && |
692 | 0 | VSI_ISDIR(sStatBuf.st_mode)) |
693 | 0 | { |
694 | 0 | memmove(frameEntry->directory, frameEntry->directory + 1, |
695 | 0 | strlen(frameEntry->directory + 1) + 1); |
696 | 0 | } |
697 | 0 | } |
698 | |
|
699 | 0 | { |
700 | 0 | char *baseDir = CPLStrdup(CPLGetDirnameSafe(pszFilename).c_str()); |
701 | 0 | VSIStatBufL sStatBuf; |
702 | 0 | char *subdir = nullptr; |
703 | 0 | if (CPLIsFilenameRelative(frameEntry->directory) == FALSE) |
704 | 0 | subdir = CPLStrdup(frameEntry->directory); |
705 | 0 | else if (frameEntry->directory[0] == '.' && |
706 | 0 | frameEntry->directory[1] == 0) |
707 | 0 | subdir = CPLStrdup(baseDir); |
708 | 0 | else |
709 | 0 | subdir = CPLStrdup( |
710 | 0 | CPLFormFilenameSafe(baseDir, frameEntry->directory, nullptr) |
711 | 0 | .c_str()); |
712 | 0 | #if !defined(_WIN32) && !defined(_WIN32_CE) |
713 | 0 | if (VSIStatL(subdir, &sStatBuf) != 0 && |
714 | 0 | strlen(subdir) > strlen(baseDir)) |
715 | 0 | { |
716 | 0 | char *c = subdir + strlen(baseDir) + 1; |
717 | 0 | while (*c) |
718 | 0 | { |
719 | 0 | if (*c >= 'A' && *c <= 'Z') |
720 | 0 | *c += 'a' - 'A'; |
721 | 0 | c++; |
722 | 0 | } |
723 | 0 | } |
724 | 0 | #endif |
725 | 0 | frameEntry->fullFilePath = CPLStrdup( |
726 | 0 | CPLFormFilenameSafe(subdir, frameEntry->filename, nullptr) |
727 | 0 | .c_str()); |
728 | 0 | if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0) |
729 | 0 | { |
730 | 0 | #if !defined(_WIN32) && !defined(_WIN32_CE) |
731 | 0 | if (strlen(frameEntry->fullFilePath) > strlen(subdir)) |
732 | 0 | { |
733 | 0 | char *c = frameEntry->fullFilePath + strlen(subdir) + 1; |
734 | 0 | while (*c) |
735 | 0 | { |
736 | 0 | if (*c >= 'A' && *c <= 'Z') |
737 | 0 | *c += 'a' - 'A'; |
738 | 0 | c++; |
739 | 0 | } |
740 | 0 | } |
741 | 0 | if (VSIStatL(frameEntry->fullFilePath, &sStatBuf) != 0) |
742 | 0 | #endif |
743 | 0 | { |
744 | 0 | frameEntry->fileExists = 0; |
745 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
746 | 0 | "File %s does not exist.", |
747 | 0 | frameEntry->fullFilePath); |
748 | 0 | } |
749 | 0 | #if !defined(_WIN32) && !defined(_WIN32_CE) |
750 | 0 | else |
751 | 0 | { |
752 | 0 | frameEntry->fileExists = 1; |
753 | 0 | } |
754 | 0 | #endif |
755 | 0 | } |
756 | 0 | else |
757 | 0 | { |
758 | 0 | frameEntry->fileExists = 1; |
759 | 0 | } |
760 | 0 | CPLFree(subdir); |
761 | 0 | CPLFree(baseDir); |
762 | 0 | } |
763 | |
|
764 | 0 | CPLDebug("RPFTOC", "Entry %d : %s,%s (%d, %d)", boundaryId, |
765 | 0 | frameEntry->directory, frameEntry->filename, frameRow, |
766 | 0 | frameCol); |
767 | |
|
768 | 0 | frameEntry->exists = 1; |
769 | 0 | } |
770 | | |
771 | 0 | return toc; |
772 | 0 | } |
773 | | |
774 | | /************************************************************************/ |
775 | | /* RPFTOCFree() */ |
776 | | /************************************************************************/ |
777 | | |
778 | | void RPFTOCFree(RPFToc *toc) |
779 | 0 | { |
780 | 0 | if (!toc) |
781 | 0 | return; |
782 | | |
783 | 0 | for (int i = 0; i < toc->nEntries; i++) |
784 | 0 | { |
785 | 0 | for (int j = 0; j < static_cast<int>(toc->entries[i].nVertFrames * |
786 | 0 | toc->entries[i].nHorizFrames); |
787 | 0 | j++) |
788 | 0 | { |
789 | 0 | CPLFree(toc->entries[i].frameEntries[j].fullFilePath); |
790 | 0 | CPLFree(toc->entries[i].frameEntries[j].directory); |
791 | 0 | } |
792 | 0 | CPLFree(toc->entries[i].frameEntries); |
793 | 0 | } |
794 | |
|
795 | 0 | CPLFree(toc->entries); |
796 | 0 | CPLFree(toc); |
797 | 0 | } |