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  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 /* Portions of code are placed under the following copyright : */
31 /*
32  ******************************************************************************
33  * Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc
34  * Permission to use, copy, modify and distribute this software and
35  * its documentation for any purpose and without fee is hereby granted,
36  * provided that the above copyright notice appear in all copies, that
37  * both the copyright notice and this permission notice appear in
38  * supporting documentation, and that the name of L.A.S. Inc not be used
39  * in advertising or publicity pertaining to distribution of the software
40  * without specific, written prior permission. L.A.S. Inc. makes no
41  * representations about the suitability of this software for any purpose.
42  * It is provided "as is" without express or implied warranty.
43  ******************************************************************************
44  */
45 
46 #include "cpl_port.h"
47 #include "rpftoclib.h"
48 
49 #include <climits>
50 #include <cstring>
51 #if HAVE_FCNTL_H
52 #  include <fcntl.h>
53 #endif
54 
55 #include "cpl_conv.h"
56 #include "cpl_error.h"
57 #include "cpl_string.h"
58 #include "cpl_vsi.h"
59 #include "nitflib.h"
60 
61 CPL_CVSID("$Id: rpftocfile.cpp df398e80769422a4bbd5d4a295f4ede443c9fec6 2021-04-04 00:17:15 +0200 Even Rouault $")
62 
63 /************************************************************************/
64 /*                        RPFTOCTrim()                                    */
65 /************************************************************************/
66 
RPFTOCTrim(char * str)67 static void RPFTOCTrim(char* str)
68 {
69     char* c = str;
70     if (str == nullptr || *str == 0)
71         return;
72 
73     while(*c == ' ')
74     {
75         c++;
76     }
77     if (c != str)
78     {
79         memmove(str, c, strlen(c)+1);
80     }
81 
82     int i = static_cast<int>(strlen(str)) - 1;
83     while (i >= 0 && str[i] == ' ')
84     {
85         str[i] = 0;
86         i--;
87     }
88 }
89 
90 /************************************************************************/
91 /*                        RPFTOCRead()                                 */
92 /************************************************************************/
93 
RPFTOCRead(const char * pszFilename,NITFFile * psFile)94 RPFToc* RPFTOCRead(const char* pszFilename, NITFFile* psFile)
95 {
96     int nTRESize;
97     const char* pachTRE = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes,
98                            "RPFHDR", &nTRESize );
99     if (pachTRE == nullptr)
100     {
101         CPLError( CE_Failure, CPLE_NotSupported,
102                   "Invalid TOC file. Can't find RPFHDR." );
103         return nullptr;
104     }
105 
106     if (nTRESize != 48)
107     {
108         CPLError( CE_Failure, CPLE_NotSupported,
109                   "RPFHDR TRE wrong size." );
110         return nullptr;
111     }
112 
113     return  RPFTOCReadFromBuffer(pszFilename, psFile->fp, pachTRE);
114 }
115 
116 /* This function is directly inspired by function parse_toc coming from ogdi/driver/rpf/utils.c */
117 
RPFTOCReadFromBuffer(const char * pszFilename,VSILFILE * fp,const char * tocHeader)118 RPFToc* RPFTOCReadFromBuffer(const char* pszFilename, VSILFILE* fp, const char* tocHeader)
119 {
120     tocHeader += 1; /* skip endian */
121     tocHeader += 2; /* skip header length */
122     tocHeader += 12; /* skip file name : this should be A.TOC (padded) */
123     tocHeader += 1; /* skip new  */
124     tocHeader += 15; /* skip standard_num  */
125     tocHeader += 8; /* skip standard_date  */
126     tocHeader += 1; /* skip classification  */
127     tocHeader += 2; /* skip country  */
128     tocHeader += 2; /* skip release  */
129 
130     unsigned int locationSectionPhysicalLocation;
131     memcpy(&locationSectionPhysicalLocation, tocHeader, sizeof(unsigned int));
132     CPL_MSBPTR32(&locationSectionPhysicalLocation);
133 
134     if( VSIFSeekL( fp, locationSectionPhysicalLocation, SEEK_SET ) != 0)
135     {
136         CPLError( CE_Failure, CPLE_NotSupported,
137                   "Invalid TOC file. Unable to seek to locationSectionPhysicalLocation at offset %d.",
138                    locationSectionPhysicalLocation );
139         return nullptr;
140     }
141 
142     int nSections;
143     NITFLocation* pasLocations = NITFReadRPFLocationTable(fp, &nSections);
144 
145     unsigned int boundaryRectangleSectionSubHeaderPhysIndex = 0;
146     unsigned int boundaryRectangleTablePhysIndex = 0;
147     unsigned int frameFileIndexSectionSubHeaderPhysIndex = 0;
148     unsigned int frameFileIndexSubsectionPhysIndex = 0;
149 
150     for( int i = 0; i < nSections; i++ )
151     {
152         if (pasLocations[i].nLocId == LID_BoundaryRectangleSectionSubheader)
153         {
154             boundaryRectangleSectionSubHeaderPhysIndex = pasLocations[i].nLocOffset;
155         }
156         else if (pasLocations[i].nLocId == LID_BoundaryRectangleTable)
157         {
158             boundaryRectangleTablePhysIndex = pasLocations[i].nLocOffset;
159         }
160         else if (pasLocations[i].nLocId == LID_FrameFileIndexSectionSubHeader)
161         {
162             frameFileIndexSectionSubHeaderPhysIndex = pasLocations[i].nLocOffset;
163         }
164         else if (pasLocations[i].nLocId == LID_FrameFileIndexSubsection)
165         {
166             frameFileIndexSubsectionPhysIndex = pasLocations[i].nLocOffset;
167         }
168     }
169 
170     CPLFree(pasLocations);
171 
172     if (boundaryRectangleSectionSubHeaderPhysIndex == 0)
173     {
174         CPLError( CE_Failure, CPLE_NotSupported,
175                   "Invalid TOC file. Can't find LID_BoundaryRectangleSectionSubheader." );
176         return nullptr;
177     }
178     if (boundaryRectangleTablePhysIndex == 0)
179     {
180         CPLError( CE_Failure, CPLE_NotSupported,
181                   "Invalid TOC file. Can't find LID_BoundaryRectangleTable." );
182         return nullptr;
183     }
184     if (frameFileIndexSectionSubHeaderPhysIndex == 0)
185     {
186         CPLError( CE_Failure, CPLE_NotSupported,
187                   "Invalid TOC file. Can't find LID_FrameFileIndexSectionSubHeader." );
188         return nullptr;
189     }
190     if (frameFileIndexSubsectionPhysIndex == 0)
191     {
192         CPLError( CE_Failure, CPLE_NotSupported,
193                   "Invalid TOC file. Can't find LID_FrameFileIndexSubsection." );
194         return nullptr;
195     }
196 
197     if( VSIFSeekL( fp, boundaryRectangleSectionSubHeaderPhysIndex, SEEK_SET ) != 0)
198     {
199         CPLError( CE_Failure, CPLE_NotSupported,
200                   "Invalid TOC file. Unable to seek to boundaryRectangleSectionSubHeaderPhysIndex at offset %d.",
201                    boundaryRectangleSectionSubHeaderPhysIndex );
202         return nullptr;
203     }
204 
205     unsigned int boundaryRectangleTableOffset;
206     bool bOK = VSIFReadL( &boundaryRectangleTableOffset, sizeof(boundaryRectangleTableOffset), 1, fp) == 1;
207     CPL_MSBPTR32( &boundaryRectangleTableOffset );
208 
209     unsigned short boundaryRectangleCount;
210     bOK &= VSIFReadL( &boundaryRectangleCount, sizeof(boundaryRectangleCount), 1, fp) == 1;
211     CPL_MSBPTR16( &boundaryRectangleCount );
212 
213     if( !bOK || VSIFSeekL( fp, boundaryRectangleTablePhysIndex, SEEK_SET ) != 0)
214     {
215         CPLError( CE_Failure, CPLE_NotSupported,
216                   "Invalid TOC file. Unable to seek to boundaryRectangleTablePhysIndex at offset %d.",
217                    boundaryRectangleTablePhysIndex );
218         return nullptr;
219     }
220 
221     RPFToc* toc = reinterpret_cast<RPFToc *>( CPLMalloc( sizeof( RPFToc ) ) );
222     toc->nEntries = boundaryRectangleCount;
223     toc->entries = reinterpret_cast<RPFTocEntry *>(
224         CPLMalloc( boundaryRectangleCount * sizeof(RPFTocEntry) ) );
225     memset(toc->entries, 0, boundaryRectangleCount * sizeof(RPFTocEntry));
226 
227     for( int i = 0; i < toc->nEntries; i++ )
228     {
229         toc->entries[i].isOverviewOrLegend = 0;
230 
231         bOK &= VSIFReadL( toc->entries[i].type, 1, 5, fp) == 5;
232         toc->entries[i].type[5] = 0;
233         RPFTOCTrim(toc->entries[i].type);
234 
235         bOK &= VSIFReadL( toc->entries[i].compression, 1, 5, fp) == 5;
236         toc->entries[i].compression[5] = 0;
237         RPFTOCTrim(toc->entries[i].compression);
238 
239         bOK &= VSIFReadL( toc->entries[i].scale, 1, 12, fp) == 12;
240         toc->entries[i].scale[12] = 0;
241         RPFTOCTrim(toc->entries[i].scale);
242         if (toc->entries[i].scale[0] == '1' &&
243             toc->entries[i].scale[1] == ':')
244         {
245             memmove(toc->entries[i].scale,
246                     toc->entries[i].scale+2,
247                     strlen(toc->entries[i].scale+2)+1);
248         }
249 
250         bOK &= VSIFReadL( toc->entries[i].zone, 1, 1, fp) == 1;
251         toc->entries[i].zone[1] = 0;
252         RPFTOCTrim(toc->entries[i].zone);
253 
254         bOK &= VSIFReadL( toc->entries[i].producer, 1, 5, fp) == 5;
255         toc->entries[i].producer[5] = 0;
256         RPFTOCTrim(toc->entries[i].producer);
257 
258         bOK &= VSIFReadL( &toc->entries[i].nwLat, sizeof(double), 1, fp) == 1;
259         CPL_MSBPTR64( &toc->entries[i].nwLat);
260 
261         bOK &= VSIFReadL( &toc->entries[i].nwLong, sizeof(double), 1, fp) == 1;
262         CPL_MSBPTR64( &toc->entries[i].nwLong);
263 
264         bOK &= VSIFReadL( &toc->entries[i].swLat, sizeof(double), 1, fp) == 1;
265         CPL_MSBPTR64( &toc->entries[i].swLat);
266 
267         bOK &= VSIFReadL( &toc->entries[i].swLong, sizeof(double), 1, fp) == 1;
268         CPL_MSBPTR64( &toc->entries[i].swLong);
269 
270         bOK &= VSIFReadL( &toc->entries[i].neLat, sizeof(double), 1, fp) == 1;
271         CPL_MSBPTR64( &toc->entries[i].neLat);
272 
273         bOK &= VSIFReadL( &toc->entries[i].neLong, sizeof(double), 1, fp) == 1;
274         CPL_MSBPTR64( &toc->entries[i].neLong);
275 
276         bOK &= VSIFReadL( &toc->entries[i].seLat, sizeof(double), 1, fp) == 1;
277         CPL_MSBPTR64( &toc->entries[i].seLat);
278 
279         bOK &= VSIFReadL( &toc->entries[i].seLong, sizeof(double), 1, fp) == 1;
280         CPL_MSBPTR64( &toc->entries[i].seLong);
281 
282         bOK &= VSIFReadL( &toc->entries[i].vertResolution, sizeof(double), 1, fp) == 1;
283         CPL_MSBPTR64( &toc->entries[i].vertResolution);
284 
285         bOK &= VSIFReadL( &toc->entries[i].horizResolution, sizeof(double), 1, fp) == 1;
286         CPL_MSBPTR64( &toc->entries[i].horizResolution);
287 
288         bOK &= VSIFReadL( &toc->entries[i].vertInterval, sizeof(double), 1, fp) == 1;
289         CPL_MSBPTR64( &toc->entries[i].vertInterval);
290 
291         bOK &= VSIFReadL( &toc->entries[i].horizInterval, sizeof(double), 1, fp) == 1;
292         CPL_MSBPTR64( &toc->entries[i].horizInterval);
293 
294         bOK &= VSIFReadL( &toc->entries[i].nVertFrames, sizeof(int), 1, fp) == 1;
295         CPL_MSBPTR32( &toc->entries[i].nVertFrames );
296 
297         bOK &= VSIFReadL( &toc->entries[i].nHorizFrames, sizeof(int), 1, fp) == 1;
298         CPL_MSBPTR32( &toc->entries[i].nHorizFrames );
299 
300         if( !bOK )
301         {
302             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
303             toc->entries[i].nVertFrames = 0;
304             toc->entries[i].nHorizFrames = 0;
305             RPFTOCFree(toc);
306             return nullptr;
307         }
308 
309         if( toc->entries[i].vertInterval <= 1e-10 ||
310             !CPLIsFinite(toc->entries[i].vertInterval) ||
311             toc->entries[i].horizInterval <= 1e-10 ||
312             !CPLIsFinite(toc->entries[i].horizInterval) ||
313             !(fabs(toc->entries[i].seLong) <= 360.0) ||
314             !(fabs(toc->entries[i].nwLong) <= 360.0) ||
315             !(fabs(toc->entries[i].nwLat) <= 90.0) ||
316             !(fabs(toc->entries[i].seLat) <= 90.0) ||
317             toc->entries[i].seLong < toc->entries[i].nwLong ||
318             toc->entries[i].nwLat < toc->entries[i].seLat ||
319             toc->entries[i].nHorizFrames == 0 ||
320             toc->entries[i].nVertFrames == 0 ||
321             toc->entries[i].nHorizFrames > INT_MAX / toc->entries[i].nVertFrames )
322         {
323             CPLError(CE_Failure, CPLE_FileIO, "Invalid TOC entry");
324             toc->entries[i].nVertFrames = 0;
325             toc->entries[i].nHorizFrames = 0;
326             RPFTOCFree(toc);
327             return nullptr;
328         }
329 
330         // TODO: We could probably use another data structure, like a list,
331         // instead of an array referenced by the frame coordinate...
332         if( static_cast<int>(toc->entries[i].nHorizFrames *
333                                   toc->entries[i].nVertFrames) >
334                  atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")) )
335         {
336             CPLError(CE_Failure, CPLE_AppDefined,
337                      "nHorizFrames=%d x nVertFrames=%d > %d. Please raise "
338                      "the value of the RPFTOC_MAX_FRAME_COUNT configuration "
339                      "option to more than %d if this dataset is legitimate.",
340                      toc->entries[i].nHorizFrames, toc->entries[i].nVertFrames,
341                      atoi(CPLGetConfigOption("RPFTOC_MAX_FRAME_COUNT", "1000000")),
342                      toc->entries[i].nHorizFrames * toc->entries[i].nVertFrames );
343             toc->entries[i].frameEntries = nullptr;
344         }
345         else
346         {
347             toc->entries[i].frameEntries = reinterpret_cast<RPFTocFrameEntry*>(
348                 VSI_CALLOC_VERBOSE( toc->entries[i].nVertFrames * toc->entries[i].nHorizFrames,
349                                     sizeof(RPFTocFrameEntry) ) );
350         }
351         if (toc->entries[i].frameEntries == nullptr)
352         {
353             toc->entries[i].nVertFrames = 0;
354             toc->entries[i].nHorizFrames = 0;
355             RPFTOCFree(toc);
356             return nullptr;
357         }
358 
359         CPLDebug("RPFTOC", "[%d] type=%s, compression=%s, scale=%s, zone=%s, producer=%s, nVertFrames=%d, nHorizFrames=%d",
360                  i, toc->entries[i].type, toc->entries[i].compression, toc->entries[i].scale,
361                  toc->entries[i].zone, toc->entries[i].producer, toc->entries[i].nVertFrames, toc->entries[i].nHorizFrames);
362     }
363 
364     if( VSIFSeekL( fp, frameFileIndexSectionSubHeaderPhysIndex, SEEK_SET ) != 0)
365     {
366         CPLError( CE_Failure, CPLE_NotSupported,
367                   "Invalid TOC file. Unable to seek to frameFileIndexSectionSubHeaderPhysIndex at offset %d.",
368                    frameFileIndexSectionSubHeaderPhysIndex );
369         RPFTOCFree(toc);
370         return nullptr;
371     }
372 
373     /* Skip 1 byte security classification */
374     bOK &= VSIFSeekL( fp, 1, SEEK_CUR ) == 0;
375 
376     unsigned int frameIndexTableOffset;
377     bOK &= VSIFReadL( &frameIndexTableOffset, sizeof(frameIndexTableOffset), 1, fp) == 1;
378     CPL_MSBPTR32( &frameIndexTableOffset );
379 
380     unsigned int nFrameFileIndexRecords;
381     bOK &= VSIFReadL( &nFrameFileIndexRecords, sizeof(nFrameFileIndexRecords), 1, fp) == 1;
382     CPL_MSBPTR32( &nFrameFileIndexRecords );
383 
384     unsigned short nFrameFilePathnameRecords;
385     bOK &= VSIFReadL( &nFrameFilePathnameRecords, sizeof(nFrameFilePathnameRecords), 1, fp) == 1;
386     CPL_MSBPTR16( &nFrameFilePathnameRecords );
387 
388     unsigned short frameFileIndexRecordLength;
389     bOK &= VSIFReadL( &frameFileIndexRecordLength, sizeof(frameFileIndexRecordLength), 1, fp) == 1;
390     CPL_MSBPTR16( &frameFileIndexRecordLength );
391     if( frameFileIndexRecordLength < 3 * sizeof(short) )
392     {
393         CPLError(CE_Failure, CPLE_FileIO, "Invalid file");
394         RPFTOCFree(toc);
395         return nullptr;
396     }
397 
398     if( !bOK )
399     {
400         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
401         RPFTOCFree(toc);
402         return nullptr;
403     }
404 
405     int newBoundaryId = 0;
406 
407     for( int i = 0; i < static_cast<int>( nFrameFileIndexRecords ); i++ )
408     {
409         vsi_l_offset nFrameOffset = static_cast<vsi_l_offset>(
410             frameFileIndexSubsectionPhysIndex) + static_cast<vsi_l_offset>(frameFileIndexRecordLength) * i;
411         if( VSIFSeekL( fp, nFrameOffset, SEEK_SET ) != 0)
412         {
413             CPLError( CE_Failure, CPLE_NotSupported,
414                     "Invalid TOC file. Unable to seek to frameFileIndexSubsectionPhysIndex(%d) at offset " CPL_FRMT_GUIB ".",
415                      i, static_cast<GUIntBig>(nFrameOffset));
416             RPFTOCFree(toc);
417             return nullptr;
418         }
419 
420         unsigned short boundaryId;
421         if( VSIFReadL( &boundaryId, sizeof(boundaryId), 1, fp) != 1 )
422         {
423             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
424             RPFTOCFree(toc);
425             return nullptr;
426         }
427         CPL_MSBPTR16( &boundaryId );
428 
429         if (i == 0 && boundaryId == 0)
430             newBoundaryId = 1;
431         if (newBoundaryId == 0)
432             boundaryId--;
433 
434         if (boundaryId >= toc->nEntries)
435         {
436             CPLError( CE_Failure, CPLE_NotSupported,
437                     "Invalid TOC file. Bad boundary id (%d) for frame file index %d.",
438                      boundaryId, i);
439             RPFTOCFree(toc);
440             return nullptr;
441         }
442 
443         RPFTocEntry* entry = &toc->entries[boundaryId];
444         entry->boundaryId = boundaryId;
445 
446         unsigned short frameRow;
447         bOK &= VSIFReadL( &frameRow, sizeof(frameRow), 1, fp) == 1;
448         CPL_MSBPTR16( &frameRow );
449 
450         unsigned short frameCol;
451         bOK &= VSIFReadL( &frameCol, sizeof(frameCol), 1, fp) == 1;
452         CPL_MSBPTR16( &frameCol );
453         if( !bOK )
454         {
455             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
456             RPFTOCFree(toc);
457             return nullptr;
458         }
459 
460         if (newBoundaryId == 0)
461         {
462             frameRow--;
463             frameCol--;
464         }
465         else
466         {
467             /* Trick so that frames are numbered north to south */
468             if( entry->nVertFrames-1 < frameRow )
469             {
470                 CPLError(CE_Failure, CPLE_FileIO, "Invalid nVertFrames vs frameRow");
471                 RPFTOCFree(toc);
472                 return nullptr;
473             }
474             frameRow = (unsigned short)((entry->nVertFrames-1) - frameRow);
475         }
476 
477         if (frameRow >= entry->nVertFrames)
478         {
479             CPLError( CE_Failure, CPLE_NotSupported,
480                         "Invalid TOC file. Bad row num (%d) for frame file index %d.",
481                         frameRow, i);
482             RPFTOCFree(toc);
483             return nullptr;
484         }
485 
486         if (frameCol >= entry->nHorizFrames)
487         {
488             CPLError( CE_Failure, CPLE_NotSupported,
489                         "Invalid TOC file. Bad col num (%d) for frame file index %d.",
490                         frameCol, i);
491             RPFTOCFree(toc);
492             return nullptr;
493         }
494 
495         RPFTocFrameEntry* frameEntry
496             = &entry->frameEntries[frameRow * entry->nHorizFrames + frameCol ];
497         frameEntry->frameRow = frameRow;
498         frameEntry->frameCol = frameCol;
499 
500         if (frameEntry->exists)
501         {
502             CPLError( CE_Warning, CPLE_AppDefined,
503                       "Frame entry(%d,%d) for frame file index %d was already found.",
504                       frameRow, frameCol, i);
505             CPLFree(frameEntry->directory);
506             frameEntry->directory = nullptr;
507             CPLFree(frameEntry->fullFilePath);
508             frameEntry->fullFilePath = nullptr;
509             frameEntry->exists = 0;
510         }
511 
512         unsigned int offsetFrameFilePathName;
513         bOK &= VSIFReadL( &offsetFrameFilePathName, sizeof(offsetFrameFilePathName), 1, fp) == 1;
514         CPL_MSBPTR32( &offsetFrameFilePathName );
515 
516         bOK &= VSIFReadL( frameEntry->filename, 1, 12, fp) == 12;
517         if( !bOK )
518         {
519             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
520             RPFTOCFree(toc);
521             return nullptr;
522         }
523         frameEntry->filename[12] = '\0';
524         bOK &= strlen(frameEntry->filename) > 0;
525 
526         /* Check if the filename is an overview or legend */
527         for( int j = 0; j < 12; j++ )
528         {
529             if (strcmp(&(frameEntry->filename[j]),".OVR") == 0 ||
530                 strcmp(&(frameEntry->filename[j]),".ovr") == 0 ||
531                 strcmp(&(frameEntry->filename[j]),".LGD") == 0 ||
532                 strcmp(&(frameEntry->filename[j]),".lgd") == 0)
533             {
534                 entry->isOverviewOrLegend = TRUE;
535                 break;
536             }
537         }
538 
539         /* Extract series code */
540         if (entry->seriesAbbreviation == nullptr)
541         {
542             const NITFSeries* series = NITFGetSeriesInfo(frameEntry->filename);
543             if (series)
544             {
545                 entry->seriesAbbreviation = series->abbreviation;
546                 entry->seriesName = series->name;
547             }
548         }
549 
550         /* Get file geo reference */
551         bOK &= VSIFReadL( frameEntry->georef, 1, 6, fp) == 6;
552         frameEntry->georef[6] = '\0';
553 
554         /* Go to start of pathname record */
555         /* New path_off offset from start of frame file index section of TOC?? */
556         /* Add pathoffset wrt frame file index table subsection (loc[3]) */
557         if( !bOK || VSIFSeekL( fp, static_cast<vsi_l_offset>(frameFileIndexSubsectionPhysIndex) + offsetFrameFilePathName, SEEK_SET ) != 0)
558         {
559             CPLError( CE_Failure, CPLE_NotSupported,
560                       "Invalid TOC file. Unable to seek to "
561                       "frameFileIndexSubsectionPhysIndex + "
562                       "offsetFrameFilePathName(%d) at offset " CPL_FRMT_GUIB ".",
563                      i, static_cast<GUIntBig>(frameFileIndexSubsectionPhysIndex) + offsetFrameFilePathName);
564             RPFTOCFree(toc);
565             return nullptr;
566         }
567 
568         unsigned short pathLength;
569         bOK &= VSIFReadL( &pathLength, sizeof(pathLength), 1, fp) == 1;
570         CPL_MSBPTR16( &pathLength );
571 
572         /* if nFrameFileIndexRecords == 65535 and pathLength == 65535 for each record,
573            this leads to 4 GB allocation... Protect against this case */
574         if (!bOK || pathLength == 0 || pathLength > 256)
575         {
576             CPLError( CE_Failure, CPLE_NotSupported,
577                       "Path length is invalid : %d. Probably corrupted TOC file.",
578                       static_cast<int>( pathLength ) );
579             RPFTOCFree(toc);
580             return nullptr;
581         }
582 
583         frameEntry->directory = reinterpret_cast<char *>( CPLMalloc(pathLength+1) );
584         bOK &= VSIFReadL( frameEntry->directory, 1, pathLength, fp) == pathLength;
585         if( !bOK )
586         {
587             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
588             RPFTOCFree(toc);
589             return nullptr;
590         }
591         frameEntry->directory[pathLength] = 0;
592         if (frameEntry->directory[pathLength-1] == '/')
593             frameEntry->directory[pathLength-1] = 0;
594 
595         if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == '/')
596         {
597             memmove(frameEntry->directory, frameEntry->directory+2, strlen(frameEntry->directory+2)+1);
598 
599             // Some A.TOC have subdirectory names like ".//X/" ... (#5979)
600             // Check if it was not intended to be "./X/" instead.
601             VSIStatBufL sStatBuf;
602             if( frameEntry->directory[0] == '/' &&
603                 VSIStatL(CPLFormFilename(CPLGetDirname(pszFilename), frameEntry->directory+1, nullptr), &sStatBuf) == 0 &&
604                 VSI_ISDIR(sStatBuf.st_mode) )
605             {
606                 memmove(frameEntry->directory, frameEntry->directory+1, strlen(frameEntry->directory+1)+1);
607             }
608         }
609 
610         {
611             char* baseDir = CPLStrdup(CPLGetDirname(pszFilename));
612             VSIStatBufL sStatBuf;
613             char* subdir = nullptr;
614             if (CPLIsFilenameRelative(frameEntry->directory) == FALSE)
615                 subdir = CPLStrdup(frameEntry->directory);
616             else if (frameEntry->directory[0] == '.' && frameEntry->directory[1] == 0)
617                 subdir = CPLStrdup(baseDir);
618             else
619                 subdir = CPLStrdup(CPLFormFilename(baseDir, frameEntry->directory, nullptr));
620 #if !defined(_WIN32) && !defined(_WIN32_CE)
621             if( VSIStatL( subdir, &sStatBuf ) != 0 && strlen(subdir) > strlen(baseDir) && subdir[strlen(baseDir)] != 0)
622             {
623                 char* c = subdir + strlen(baseDir)+1;
624                 while(*c)
625                 {
626                     if (*c >= 'A' && *c <= 'Z')
627                         *c += 'a' - 'A';
628                     c++;
629                 }
630             }
631 #endif
632             frameEntry->fullFilePath = CPLStrdup(CPLFormFilename(
633                     subdir,
634                     frameEntry->filename, nullptr));
635             if( VSIStatL( frameEntry->fullFilePath, &sStatBuf ) != 0 )
636             {
637 #if !defined(_WIN32) && !defined(_WIN32_CE)
638                 if( strlen(frameEntry->fullFilePath) > strlen(subdir) )
639                 {
640                     char* c = frameEntry->fullFilePath + strlen(subdir)+1;
641                     while(*c)
642                     {
643                         if (*c >= 'A' && *c <= 'Z')
644                             *c += 'a' - 'A';
645                         c++;
646                     }
647                 }
648                 if( VSIStatL( frameEntry->fullFilePath, &sStatBuf ) != 0 )
649 #endif
650                 {
651                     frameEntry->fileExists = 0;
652                     CPLError( CE_Warning, CPLE_AppDefined,
653                         "File %s does not exist.", frameEntry->fullFilePath );
654                 }
655 #if !defined(_WIN32) && !defined(_WIN32_CE)
656                 else
657                 {
658                     frameEntry->fileExists = 1;
659                 }
660 #endif
661             }
662             else
663             {
664                 frameEntry->fileExists = 1;
665             }
666             CPLFree(subdir);
667             CPLFree(baseDir);
668         }
669 
670         CPLDebug("RPFTOC", "Entry %d : %s,%s (%d, %d)", boundaryId, frameEntry->directory, frameEntry->filename, frameRow, frameCol);
671 
672         frameEntry->exists = 1;
673     }
674 
675     return toc;
676 }
677 
678 /************************************************************************/
679 /*                        RPFTOCFree()                                 */
680 /************************************************************************/
681 
RPFTOCFree(RPFToc * toc)682 void RPFTOCFree(RPFToc*  toc)
683 {
684     if (!toc)
685         return;
686 
687     for( int i = 0; i < toc->nEntries; i++ )
688     {
689         for( int j = 0;
690              j < static_cast<int>(toc->entries[i].nVertFrames
691                        * toc->entries[i].nHorizFrames);
692              j++)
693         {
694             CPLFree(toc->entries[i].frameEntries[j].fullFilePath);
695             CPLFree(toc->entries[i].frameEntries[j].directory);
696         }
697         CPLFree(toc->entries[i].frameEntries);
698     }
699 
700     CPLFree(toc->entries);
701     CPLFree(toc);
702 }
703