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