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