Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/avc/avc_bin.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Name:     avc_bin.c
4
 * Project:  Arc/Info vector coverage (AVC)  BIN->E00 conversion library
5
 * Language: ANSI C
6
 * Purpose:  Binary files access functions.
7
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
8
 *
9
 **********************************************************************
10
 * Copyright (c) 1999-2005, Daniel Morissette
11
 *
12
 * SPDX-License-Identifier: MIT
13
 **********************************************************************
14
 *
15
 * $Log: avc_bin.c,v $
16
 * Revision 1.30  2008/07/23 20:51:38  dmorissette
17
 * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char
18
 * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495)
19
 *
20
 * Revision 1.29  2006/08/17 18:56:42  dmorissette
21
 * Support for reading standalone info tables (just tables, no coverage
22
 * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549).
23
 *
24
 * Revision 1.28  2006/06/14 16:31:28  daniel
25
 * Added support for AVCCoverPC2 type (bug 1491)
26
 *
27
 * Revision 1.27  2005/06/03 03:49:58  daniel
28
 * Update email address, website url, and copyright dates
29
 *
30
 * Revision 1.26  2004/02/28 06:35:49  warmerda
31
 * Fixed AVCBinReadObject() index support to use 'x' or 'X' for index
32
 * depending on the case of the original name.
33
 * Fixed so that PC Arc/Info coverages with the extra 256 byte header work
34
 * properly when using indexes to read them.
35
 *   http://bugzilla.remotesensing.org/show_bug.cgi?id=493
36
 *
37
 * Revision 1.25  2004/02/11 05:49:44  daniel
38
 * Added support for deleted flag in arc.dir (bug 2332)
39
 *
40
 * Revision 1.24  2002/08/27 15:26:06  daniel
41
 * Removed C++ style comments for IRIX compiler (GDAL bug 192)
42
 *
43
 * Revision 1.23  2002/04/16 20:04:24  daniel
44
 * Use record size while reading ARC, PAL, CNT to skip junk bytes. (bug940)
45
 *
46
 * Revision 1.22  2002/03/18 19:03:37  daniel
47
 * Fixed AVCBinReadObject() for PAL objects (bug 848)
48
 *
49
 * Revision 1.21  2002/02/14 22:54:13  warmerda
50
 * added polygon and table support for random reading
51
 *
52
 * Revision 1.20  2002/02/13 20:35:24  warmerda
53
 * added AVCBinReadObject
54
 *
55
 * Revision 1.19  2001/11/25 22:01:23  daniel
56
 * Fixed order of args to AVCRawBinFSeek() in _AVCBinReadNextTableRec()
57
 *
58
 * Revision 1.18  2000/10/16 16:16:20  daniel
59
 * Accept TXT files in AVCCoverWeird that use both PC or V7 TXT structure
60
 *
61
 * Revision 1.17  2000/09/26 20:21:04  daniel
62
 * Added AVCCoverPC write
63
 *
64
 * Revision 1.16  2000/09/22 19:45:20  daniel
65
 * Switch to MIT-style license
66
 *
67
 * Revision 1.15  2000/09/20 15:09:34  daniel
68
 * Check for DAT/NIT fnames sometimes truncated to 8 chars in weird coverages
69
 *
70
 * Revision 1.14  2000/06/05 21:38:53  daniel
71
 * Handle precision field > 1000 in cover file header as meaning double prec.
72
 *
73
 * Revision 1.13  2000/05/29 15:31:30  daniel
74
 * Added Japanese DBCS support
75
 *
76
 * Revision 1.12  2000/02/14 17:22:36  daniel
77
 * Check file signature (9993 or 9994) when reading header.
78
 *
79
 * Revision 1.11  2000/02/02 04:24:52  daniel
80
 * Support double precision "weird" coverages
81
 *
82
 * Revision 1.10  2000/01/10 02:54:10  daniel
83
 * Added read support for "weird" coverages
84
 *
85
 * Revision 1.9  2000/01/07 07:11:51  daniel
86
 * Added support for reading PC Coverage TXT files
87
 *
88
 * Revision 1.8  1999/12/24 07:38:10  daniel
89
 * Added missing DBFClose()
90
 *
91
 * Revision 1.7  1999/12/24 07:18:34  daniel
92
 * Added PC Arc/Info coverages support
93
 *
94
 * Revision 1.6  1999/08/23 18:17:16  daniel
95
 * Modified AVCBinReadListTables() to return INFO fnames for DeleteCoverage()
96
 *
97
 * Revision 1.5  1999/05/11 01:49:08  daniel
98
 * Simple changes required by addition of coverage write support
99
 *
100
 * Revision 1.4  1999/03/03 18:42:53  daniel
101
 * Fixed problem with INFO table headers (arc.dir) that sometimes contain an
102
 * invalid number of records.
103
 *
104
 * Revision 1.3  1999/02/25 17:01:53  daniel
105
 * Added support for 16 bit integers in INFO tables (type=50, size=2)
106
 *
107
 * Revision 1.2  1999/02/25 03:41:28  daniel
108
 * Added TXT, TX6/TX7, RXP and RPL support
109
 *
110
 * Revision 1.1  1999/01/29 16:28:52  daniel
111
 * Initial revision
112
 *
113
 **********************************************************************/
114
115
#include "avc.h"
116
117
#include <ctype.h> /* for isspace() */
118
119
#ifdef WITHOUT_SHAPEFILE
120
121
#define SHP_VSI_ONLY_SETUP_HOOKS
122
#define SAOffset vsi_l_offset
123
#define SHPAPI_CAL
124
125
#define DBFAddField OGRAVC_DBFAddField
126
#define DBFAddNativeFieldType OGRAVC_DBFAddNativeFieldType
127
#define DBFAlterFieldDefn OGRAVC_DBFAlterFieldDefn
128
#define DBFCloneEmpty OGRAVC_DBFCloneEmpty
129
#define DBFClose OGRAVC_DBFClose
130
#define DBFCreateEx OGRAVC_DBFCreateEx
131
#define DBFCreate OGRAVC_DBFCreate
132
#define DBFCreateLL OGRAVC_DBFCreateLL
133
#define DBFDeleteField OGRAVC_DBFDeleteField
134
#define DBFFlushRecord OGRAVC_DBFFlushRecord
135
#define DBFGetCodePage OGRAVC_DBFGetCodePage
136
#define DBFGetFieldCount OGRAVC_DBFGetFieldCount
137
#define DBFGetFieldIndex OGRAVC_DBFGetFieldIndex
138
#define DBFGetFieldInfo OGRAVC_DBFGetFieldInfo
139
#define DBFGetLenWithoutExtension OGRAVC_DBFGetLenWithoutExtension
140
#define DBFGetNativeFieldType OGRAVC_DBFGetNativeFieldType
141
#define DBFGetNullCharacter OGRAVC_DBFGetNullCharacter
142
#define DBFGetRecordCount OGRAVC_DBFGetRecordCount
143
#define DBFIsAttributeNULL OGRAVC_DBFIsAttributeNULL
144
#define DBFIsRecordDeleted OGRAVC_DBFIsRecordDeleted
145
#define DBFIsValueNULL OGRAVC_DBFIsValueNULL
146
#define DBFLoadRecord OGRAVC_DBFLoadRecord
147
#define DBFMarkRecordDeleted OGRAVC_DBFMarkRecordDeleted
148
#define DBFOpen OGRAVC_DBFOpen
149
#define DBFOpenLL OGRAVC_DBFOpenLL
150
#define DBFReadAttribute OGRAVC_DBFReadAttribute
151
#define DBFReadDoubleAttribute OGRAVC_DBFReadDoubleAttribute
152
#define DBFReadIntegerAttribute OGRAVC_DBFReadIntegerAttribute
153
#define DBFReadLogicalAttribute OGRAVC_DBFReadLogicalAttribute
154
#define DBFReadStringAttribute OGRAVC_DBFReadStringAttribute
155
#define DBFReadDateAttribute OGRAVC_DBFReadDateAttribute
156
#define DBFReadTuple OGRAVC_DBFReadTuple
157
#define DBFReorderFields OGRAVC_DBFReorderFields
158
#define DBFSetLastModifiedDate OGRAVC_DBFSetLastModifiedDate
159
#define DBFSetWriteEndOfFileChar OGRAVC_DBFSetWriteEndOfFileChar
160
#define DBFUpdateHeader OGRAVC_DBFUpdateHeader
161
#define DBFWriteAttributeDirectly OGRAVC_DBFWriteAttributeDirectly
162
#define DBFWriteAttribute OGRAVC_DBFWriteAttribute
163
#define DBFWriteDoubleAttribute OGRAVC_DBFWriteDoubleAttribute
164
#define DBFWriteHeader OGRAVC_DBFWriteHeader
165
#define DBFWriteIntegerAttribute OGRAVC_DBFWriteIntegerAttribute
166
#define DBFWriteLogicalAttribute OGRAVC_DBFWriteLogicalAttribute
167
#define DBFWriteNULLAttribute OGRAVC_DBFWriteNULLAttribute
168
#define DBFWriteStringAttribute OGRAVC_DBFWriteStringAttribute
169
#define DBFWriteDateAttribute OGRAVC_DBFWriteDateAttribute
170
#define DBFWriteTuple OGRAVC_DBFWriteTuple
171
172
#define VSI_SHP_WriteMoreDataOK OGRAVC_VSI_SHP_WriteMoreDataOK
173
#define SASetupDefaultHooks OGRAVC_SASetupDefaultHooks
174
175
#include "shapefil.h"
176
#include "dbfopen.c"
177
#include "shp_vsi.cpp"
178
179
#else
180
181
#ifdef RENAME_INTERNAL_SHAPELIB_SYMBOLS
182
#include "gdal_shapelib_symbol_rename.h"
183
#endif
184
#include "shapefil.h"
185
186
#endif  // WITHOUT_SHAPEFILE
187
188
/* Used by avc_binwr.c */
189
extern int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir);
190
191
/*=====================================================================
192
 * Prototypes for some static functions
193
 *====================================================================*/
194
195
static AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath,
196
                                        const char *pszTableName,
197
                                        AVCCoverType eCoverType,
198
                                        AVCDBCSInfo *psDBCSInfo);
199
static AVCBinFile *_AVCBinReadOpenDBFTable(const char *pszInfoPath,
200
                                           const char *pszTableName);
201
static AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath, const char *pszName);
202
203
static int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields,
204
                                   AVCFieldInfo *pasDef, AVCField *pasFields,
205
                                   int nRecordSize);
206
static int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex,
207
                                      int nFields, AVCFieldInfo *pasDef,
208
                                      AVCField *pasFields);
209
210
/*=====================================================================
211
 * Stuff related to reading the binary coverage files
212
 *====================================================================*/
213
214
/**********************************************************************
215
 *                          AVCBinReadOpen()
216
 *
217
 * Open a coverage file for reading, read the file header if applicable,
218
 * and initialize a temp. storage structure to be ready to read objects
219
 * from the file.
220
 *
221
 * pszPath is the coverage (or info directory) path, terminated by
222
 *         a '/' or a '\\'
223
 * pszName is the name of the file to open relative to this directory.
224
 *
225
 * Note: For most file types except tables, passing pszPath="" and
226
 * including the coverage path as part of pszName instead would work.
227
 *
228
 * Returns a valid AVCBinFile handle, or nullptr if the file could
229
 * not be opened.
230
 *
231
 * AVCBinClose() will eventually have to be called to release the
232
 * resources used by the AVCBinFile structure.
233
 **********************************************************************/
234
AVCBinFile *AVCBinReadOpen(const char *pszPath, const char *pszName,
235
                           AVCCoverType eCoverType, AVCFileType eFileType,
236
                           AVCDBCSInfo *psDBCSInfo)
237
39.1k
{
238
39.1k
    AVCBinFile *psFile;
239
240
    /*-----------------------------------------------------------------
241
     * The case of INFO tables is a bit more complicated...
242
     * pass the control to a separate function.
243
     *----------------------------------------------------------------*/
244
39.1k
    if (eFileType == AVCFileTABLE)
245
4.97k
    {
246
4.97k
        if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2)
247
289
            return _AVCBinReadOpenDBFTable(pszPath, pszName);
248
4.68k
        else
249
4.68k
            return _AVCBinReadOpenTable(pszPath, pszName, eCoverType,
250
4.68k
                                        psDBCSInfo);
251
4.97k
    }
252
253
    /*-----------------------------------------------------------------
254
     * PRJ files are text files... we won't use the AVCRawBin*()
255
     * functions for them...
256
     *----------------------------------------------------------------*/
257
34.2k
    if (eFileType == AVCFilePRJ)
258
12.0k
    {
259
12.0k
        return _AVCBinReadOpenPrj(pszPath, pszName);
260
12.0k
    }
261
262
    /*-----------------------------------------------------------------
263
     * All other file types share a very similar opening method.
264
     *----------------------------------------------------------------*/
265
22.1k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
266
267
22.1k
    psFile->eFileType = eFileType;
268
22.1k
    psFile->eCoverType = eCoverType;
269
270
22.1k
    psFile->pszFilename =
271
22.1k
        (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
272
22.1k
    snprintf(psFile->pszFilename, strlen(pszPath) + strlen(pszName) + 1, "%s%s",
273
22.1k
             pszPath, pszName);
274
275
22.1k
    AVCAdjustCaseSensitiveFilename(psFile->pszFilename);
276
277
22.1k
    psFile->psRawBinFile = AVCRawBinOpen(
278
22.1k
        psFile->pszFilename, "r", AVC_COVER_BYTE_ORDER(eCoverType), psDBCSInfo);
279
280
22.1k
    if (psFile->psRawBinFile == nullptr)
281
97
    {
282
        /* Failed to open file... just return nullptr since an error message
283
         * has already been issued by AVCRawBinOpen()
284
         */
285
97
        CPLFree(psFile->pszFilename);
286
97
        CPLFree(psFile);
287
97
        return nullptr;
288
97
    }
289
290
    /*-----------------------------------------------------------------
291
     * Read the header, and set the precision field if applicable
292
     *----------------------------------------------------------------*/
293
22.0k
    if (AVCBinReadRewind(psFile) != 0)
294
2.33k
    {
295
2.33k
        AVCRawBinClose(psFile->psRawBinFile);
296
2.33k
        CPLFree(psFile->pszFilename);
297
2.33k
        CPLFree(psFile);
298
2.33k
        return nullptr;
299
2.33k
    }
300
301
    /*-----------------------------------------------------------------
302
     * Allocate a temp. structure to use to read objects from the file
303
     * (Using Calloc() will automatically initialize the struct contents
304
     *  to nullptr... this is very important for ARCs and PALs)
305
     *----------------------------------------------------------------*/
306
19.6k
    if (psFile->eFileType == AVCFileARC)
307
1.84k
    {
308
1.84k
        psFile->cur.psArc = (AVCArc *)CPLCalloc(1, sizeof(AVCArc));
309
1.84k
    }
310
17.8k
    else if (psFile->eFileType == AVCFilePAL || psFile->eFileType == AVCFileRPL)
311
9.17k
    {
312
9.17k
        psFile->cur.psPal = (AVCPal *)CPLCalloc(1, sizeof(AVCPal));
313
9.17k
    }
314
8.66k
    else if (psFile->eFileType == AVCFileCNT)
315
500
    {
316
500
        psFile->cur.psCnt = (AVCCnt *)CPLCalloc(1, sizeof(AVCCnt));
317
500
    }
318
8.16k
    else if (psFile->eFileType == AVCFileLAB)
319
3.05k
    {
320
3.05k
        psFile->cur.psLab = (AVCLab *)CPLCalloc(1, sizeof(AVCLab));
321
3.05k
    }
322
5.10k
    else if (psFile->eFileType == AVCFileTOL)
323
33
    {
324
33
        psFile->cur.psTol = (AVCTol *)CPLCalloc(1, sizeof(AVCTol));
325
33
    }
326
5.07k
    else if (psFile->eFileType == AVCFileTXT || psFile->eFileType == AVCFileTX6)
327
4.92k
    {
328
4.92k
        psFile->cur.psTxt = (AVCTxt *)CPLCalloc(1, sizeof(AVCTxt));
329
4.92k
    }
330
147
    else if (psFile->eFileType == AVCFileRXP)
331
147
    {
332
147
        psFile->cur.psRxp = (AVCRxp *)CPLCalloc(1, sizeof(AVCRxp));
333
147
    }
334
0
    else
335
0
    {
336
0
        CPLError(CE_Failure, CPLE_IllegalArg,
337
0
                 "%s: Unsupported file type or corrupted file.",
338
0
                 psFile->pszFilename);
339
0
        AVCRawBinClose(psFile->psRawBinFile);
340
0
        CPLFree(psFile->pszFilename);
341
0
        CPLFree(psFile);
342
0
        psFile = nullptr;
343
0
    }
344
345
19.6k
    return psFile;
346
22.0k
}
347
348
/**********************************************************************
349
 *                          AVCBinReadClose()
350
 *
351
 * Close a coverage file, and release all memory (object strcut., buffers,
352
 * etc.) associated with this file.
353
 **********************************************************************/
354
void AVCBinReadClose(AVCBinFile *psFile)
355
36.3k
{
356
36.3k
    AVCRawBinClose(psFile->psRawBinFile);
357
36.3k
    psFile->psRawBinFile = nullptr;
358
359
36.3k
    CPLFree(psFile->pszFilename);
360
36.3k
    psFile->pszFilename = nullptr;
361
362
36.3k
    if (psFile->hDBFFile)
363
203
        DBFClose(psFile->hDBFFile);
364
365
36.3k
    if (psFile->psIndexFile != nullptr)
366
663
        AVCRawBinClose(psFile->psIndexFile);
367
368
36.3k
    if (psFile->eFileType == AVCFileARC)
369
1.84k
    {
370
1.84k
        if (psFile->cur.psArc)
371
1.84k
            CPLFree(psFile->cur.psArc->pasVertices);
372
1.84k
        CPLFree(psFile->cur.psArc);
373
1.84k
    }
374
34.4k
    else if (psFile->eFileType == AVCFilePAL || psFile->eFileType == AVCFileRPL)
375
9.17k
    {
376
9.17k
        if (psFile->cur.psPal)
377
9.17k
            CPLFree(psFile->cur.psPal->pasArcs);
378
9.17k
        CPLFree(psFile->cur.psPal);
379
9.17k
    }
380
25.3k
    else if (psFile->eFileType == AVCFileCNT)
381
500
    {
382
500
        if (psFile->cur.psCnt)
383
500
            CPLFree(psFile->cur.psCnt->panLabelIds);
384
500
        CPLFree(psFile->cur.psCnt);
385
500
    }
386
24.8k
    else if (psFile->eFileType == AVCFileLAB)
387
3.05k
    {
388
3.05k
        CPLFree(psFile->cur.psLab);
389
3.05k
    }
390
21.7k
    else if (psFile->eFileType == AVCFileTOL)
391
33
    {
392
33
        CPLFree(psFile->cur.psTol);
393
33
    }
394
21.7k
    else if (psFile->eFileType == AVCFilePRJ)
395
11.9k
    {
396
11.9k
        CSLDestroy(psFile->cur.papszPrj);
397
11.9k
    }
398
9.72k
    else if (psFile->eFileType == AVCFileTXT || psFile->eFileType == AVCFileTX6)
399
4.92k
    {
400
4.92k
        if (psFile->cur.psTxt)
401
4.92k
        {
402
4.92k
            CPLFree(psFile->cur.psTxt->pasVertices);
403
4.92k
            CPLFree(psFile->cur.psTxt->pszText);
404
4.92k
        }
405
4.92k
        CPLFree(psFile->cur.psTxt);
406
4.92k
    }
407
4.79k
    else if (psFile->eFileType == AVCFileRXP)
408
147
    {
409
147
        CPLFree(psFile->cur.psRxp);
410
147
    }
411
4.65k
    else if (psFile->eFileType == AVCFileTABLE)
412
4.65k
    {
413
4.65k
        _AVCDestroyTableFields(psFile->hdr.psTableDef, psFile->cur.pasFields);
414
4.65k
        _AVCDestroyTableDef(psFile->hdr.psTableDef);
415
4.65k
    }
416
0
    else
417
0
    {
418
0
        CPLError(CE_Failure, CPLE_IllegalArg,
419
0
                 "Unsupported file type or invalid file handle!");
420
0
    }
421
422
36.3k
    CPLFree(psFile);
423
36.3k
}
424
425
/**********************************************************************
426
 *                          _AVCBinReadHeader()
427
 *
428
 * (This function is for internal library use... external calls should
429
 * go to AVCBinReadRewind() instead)
430
 *
431
 * Read the first 100 bytes header of the file and fill the AVCHeader
432
 * structure.
433
 *
434
 * Returns 0 on success or -1 on error.
435
 **********************************************************************/
436
static int _AVCBinReadHeader(AVCRawBinFile *psFile, AVCBinHeader *psHeader,
437
                             AVCCoverType eCoverType)
438
21.8k
{
439
21.8k
    int nStatus = 0;
440
441
    /*-----------------------------------------------------------------
442
     * For AVCCoverPC coverages (files without the .adf extension),
443
     * there is a first 256 bytes header that we just skip and that
444
     * precedes the 100 bytes header block.
445
     *
446
     * In AVCCoverV7, we only have the 100 bytes header.
447
     *----------------------------------------------------------------*/
448
21.8k
    if (eCoverType == AVCCoverPC)
449
370
        AVCRawBinFSeek(psFile, 256, SEEK_SET);
450
21.4k
    else
451
21.4k
        AVCRawBinFSeek(psFile, 0, SEEK_SET);
452
453
21.8k
    psHeader->nSignature = AVCRawBinReadInt32(psFile);
454
455
21.8k
    if (AVCRawBinEOF(psFile))
456
26
        nStatus = -1;
457
458
21.8k
    psHeader->nPrecision = AVCRawBinReadInt32(psFile);
459
21.8k
    psHeader->nRecordSize = AVCRawBinReadInt32(psFile);
460
461
    /* Jump to 24th byte in header */
462
21.8k
    AVCRawBinFSeek(psFile, 12, SEEK_CUR);
463
21.8k
    psHeader->nLength = AVCRawBinReadInt32(psFile);
464
21.8k
    if (psHeader->nLength < 0 || psHeader->nLength > (INT_MAX - 256) / 2)
465
1.68k
    {
466
1.68k
        return -1;
467
1.68k
    }
468
469
    /*-----------------------------------------------------------------
470
     * File length, in words (16 bits)... pass the info to the RawBinFile
471
     * to prevent it from trying to read junk bytes at the end of files...
472
     * this problem happens specially with PC Arc/Info files.
473
     *----------------------------------------------------------------*/
474
20.1k
    if (eCoverType == AVCCoverPC)
475
356
        AVCRawBinSetFileDataSize(psFile, psHeader->nLength * 2 + 256);
476
19.8k
    else
477
19.8k
        AVCRawBinSetFileDataSize(psFile, psHeader->nLength * 2);
478
479
    /* Move the pointer at the end of the 100 bytes header
480
     */
481
20.1k
    AVCRawBinFSeek(psFile, 72, SEEK_CUR);
482
483
20.1k
    return nStatus;
484
21.8k
}
485
486
/**********************************************************************
487
 *                          AVCBinReadRewind()
488
 *
489
 * Rewind the read pointer, and read/skip the header if necessary so
490
 * that we are ready to read the data objects from the file after
491
 * this call.
492
 *
493
 * Returns 0 on success, -1 on error, and -2 if file has an invalid
494
 * signature and is possibly corrupted.
495
 **********************************************************************/
496
int AVCBinReadRewind(AVCBinFile *psFile)
497
22.0k
{
498
22.0k
    AVCBinHeader sHeader;
499
22.0k
    int nStatus = 0;
500
501
    /*-----------------------------------------------------------------
502
     * For AVCCoverPC coverages, there is a first 256 bytes header
503
     * that we just skip and that precedes the 100 bytes header block.
504
     *
505
     * In AVCCoverV7, AVCCoverPC2 and AVCCoverWeird, we only find the
506
     * 100 bytes header.
507
     *
508
     * Note: it is the call to _AVCBinReadHeader() that takes care
509
     * of skipping the first 256 bytes header if necessary.
510
     *----------------------------------------------------------------*/
511
512
22.0k
    AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
513
514
22.0k
    if (psFile->eFileType == AVCFileARC || psFile->eFileType == AVCFilePAL ||
515
16.7k
        psFile->eFileType == AVCFileRPL || psFile->eFileType == AVCFileCNT ||
516
8.52k
        psFile->eFileType == AVCFileLAB || psFile->eFileType == AVCFileTXT ||
517
4.84k
        psFile->eFileType == AVCFileTX6)
518
21.8k
    {
519
21.8k
        nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
520
21.8k
                                    psFile->eCoverType);
521
522
        /* Store the precision information inside the file handle.
523
         *
524
         * Of course, there had to be an exception...
525
         * At least PAL and TXT files in PC Arc/Info coverages sometimes
526
         * have a negative precision flag even if they contain single
527
         * precision data... why is that????  A PC Arc bug?
528
         *
529
         * 2000-06-05: Found a double-precision PAL file with a signature
530
         *             of 1011 (should have been -11).  So we'll assume
531
         *             that signature > 1000 also means double precision.
532
         */
533
21.8k
        if ((sHeader.nPrecision < 0 || sHeader.nPrecision > 1000) &&
534
10.9k
            psFile->eCoverType != AVCCoverPC)
535
10.6k
            psFile->nPrecision = AVC_DOUBLE_PREC;
536
11.1k
        else
537
11.1k
            psFile->nPrecision = AVC_SINGLE_PREC;
538
539
        /* Validate the signature value... this will allow us to detect
540
         * corrupted files or files that do not belong in the coverage.
541
         */
542
21.8k
        if (sHeader.nSignature != 9993 && sHeader.nSignature != 9994)
543
1.07k
        {
544
1.07k
            CPLError(CE_Warning, CPLE_AssertionFailed,
545
1.07k
                     "%s appears to have an invalid file header.",
546
1.07k
                     psFile->pszFilename);
547
1.07k
            return -2;
548
1.07k
        }
549
550
        /* In Weird coverages, TXT files can be stored in the PC or the V7
551
         * format.  Look at the 'precision' field in the header to tell which
552
         * type we have.
553
         *   Weird TXT in PC format: nPrecision = 16
554
         *   Weird TXT in V7 format: nPrecision = +/-67
555
         * Use AVCFileTXT for PC type, and AVCFileTX6 for V7 type.
556
         */
557
20.7k
        if (psFile->eCoverType == AVCCoverWeird &&
558
270
            psFile->eFileType == AVCFileTXT &&
559
60
            (sHeader.nPrecision == 67 || sHeader.nPrecision == -67))
560
3
        {
561
            /* TXT file will be processed as V7 TXT/TX6/TX7 */
562
3
            psFile->eFileType = AVCFileTX6;
563
3
        }
564
20.7k
    }
565
180
    else if (psFile->eFileType == AVCFileTOL)
566
33
    {
567
        /*-------------------------------------------------------------
568
         * For some reason, the tolerance files do not follow the
569
         * general rules!
570
         * Single precision "tol.adf" have no header
571
         * Double precision "par.adf" have the usual 100 bytes header,
572
         *  but the 3rd field, which usually defines the precision has
573
         *  a positive value, even if the file is double precision!
574
         *
575
         * Also, we have a problem with PC Arc/Info TOL files since they
576
         * do not contain the first 256 bytes header either... so we will
577
         * just assume that double precision TOL files cannot exist in
578
         * PC Arc/Info coverages... this should be OK.
579
         *------------------------------------------------------------*/
580
33
        int nSignature = 0;
581
33
        nSignature = AVCRawBinReadInt32(psFile->psRawBinFile);
582
583
33
        if (nSignature == 9993)
584
0
        {
585
            /* We have a double precision par.adf... read the 100 bytes
586
             * header and set the precision information inside the file
587
             * handle.
588
             */
589
0
            nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
590
0
                                        psFile->eCoverType);
591
592
0
            psFile->nPrecision = AVC_DOUBLE_PREC;
593
0
        }
594
33
        else
595
33
        {
596
            /* It's a single precision tol.adf ... just set the
597
             * precision field.
598
             */
599
33
            AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
600
33
            psFile->nPrecision = AVC_SINGLE_PREC;
601
33
        }
602
33
    }
603
604
20.9k
    return nStatus;
605
22.0k
}
606
607
/**********************************************************************
608
 *                          AVCBinReadObject()
609
 *
610
 * Read the object with a particular index.  For fixed length record
611
 * files we seek directly to the object.  For variable files we try to
612
 * get the offset from the corresponding index file.
613
 *
614
 * NOTE: Currently only implemented for ARC, PAL and TABLE files.
615
 *
616
 * Returns the read object on success or nullptr on error.
617
 **********************************************************************/
618
void *AVCBinReadObject(AVCBinFile *psFile, int iObjIndex)
619
183k
{
620
183k
    int bIndexed = FALSE;
621
183k
    int nObjectOffset, nRecordSize = 0, nRecordStart = 0, nLen;
622
    /* cppcheck-suppress unreadVariable */
623
183k
    char szExt[4] = {0, 0, 0, 0};
624
183k
    char *pszExt = szExt;
625
626
183k
    if (iObjIndex < 0)
627
4.28k
        return nullptr;
628
629
    /*-----------------------------------------------------------------
630
     * Determine some information from based on the coverage type.
631
     *----------------------------------------------------------------*/
632
179k
    nLen = (int)strlen(psFile->pszFilename);
633
179k
    if (psFile->eFileType == AVCFileARC &&
634
45.3k
        ((nLen >= 3 &&
635
45.3k
          STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 3), "arc")) ||
636
45.3k
         (nLen >= 7 && STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 7),
637
45.3k
                                      "arc.adf"))))
638
45.3k
    {
639
45.3k
        bIndexed = TRUE;
640
45.3k
    }
641
134k
    else if (psFile->eFileType == AVCFilePAL &&
642
0
             ((nLen >= 3 &&
643
0
               STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 3),
644
0
                              "pal")) ||
645
0
              (nLen >= 7 &&
646
0
               STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 7),
647
0
                              "pal.adf"))))
648
0
    {
649
0
        bIndexed = TRUE;
650
0
    }
651
134k
    else if (psFile->eFileType == AVCFileTABLE)
652
134k
    {
653
134k
        bIndexed = FALSE;
654
134k
        nRecordSize = psFile->hdr.psTableDef->nRecSize;
655
134k
        nRecordStart = 0;
656
134k
    }
657
0
    else
658
0
        return nullptr;
659
660
    /*-----------------------------------------------------------------
661
     * Ensure the index file is opened if an index file is required.
662
     *----------------------------------------------------------------*/
663
664
179k
    if (bIndexed && psFile->psIndexFile == nullptr)
665
2.49k
    {
666
2.49k
        char chOrig;
667
668
2.49k
        chOrig = pszExt[2];
669
2.49k
        if (chOrig > 'A' && chOrig < 'Z')
670
115
            pszExt[2] = 'X';
671
2.37k
        else
672
2.37k
            pszExt[2] = 'x';
673
674
2.49k
        psFile->psIndexFile = AVCRawBinOpen(psFile->pszFilename, "rb",
675
2.49k
                                            psFile->psRawBinFile->eByteOrder,
676
2.49k
                                            psFile->psRawBinFile->psDBCSInfo);
677
2.49k
        pszExt[2] = chOrig;
678
679
2.49k
        if (psFile->psIndexFile == nullptr)
680
1.83k
            return nullptr;
681
2.49k
    }
682
683
    /*-----------------------------------------------------------------
684
     * Establish the offset to read the object from.
685
     *----------------------------------------------------------------*/
686
177k
    if (bIndexed)
687
43.4k
    {
688
43.4k
        GIntBig nIndexOffsetBig;
689
690
43.4k
        if (psFile->eCoverType == AVCCoverPC)
691
0
            nIndexOffsetBig = 356 + static_cast<GIntBig>(iObjIndex - 1) * 8;
692
43.4k
        else
693
43.4k
            nIndexOffsetBig = 100 + static_cast<GIntBig>(iObjIndex - 1) * 8;
694
43.4k
        if (nIndexOffsetBig < INT_MIN || nIndexOffsetBig > INT_MAX)
695
635
            return nullptr;
696
697
42.8k
        const int nIndexOffset = static_cast<int>(nIndexOffsetBig);
698
42.8k
        AVCRawBinFSeek(psFile->psIndexFile, nIndexOffset, SEEK_SET);
699
42.8k
        if (AVCRawBinEOF(psFile->psIndexFile))
700
2.23k
            return nullptr;
701
702
40.6k
        nObjectOffset = AVCRawBinReadInt32(psFile->psIndexFile);
703
40.6k
        if (nObjectOffset < INT_MIN / 2 || nObjectOffset > (INT_MAX - 256) / 2)
704
701
            return nullptr;
705
39.9k
        nObjectOffset *= 2;
706
707
39.9k
        if (psFile->eCoverType == AVCCoverPC)
708
0
            nObjectOffset += 256;
709
39.9k
    }
710
134k
    else
711
134k
    {
712
134k
        GIntBig nObjectOffsetBig =
713
134k
            nRecordStart + nRecordSize * static_cast<GIntBig>(iObjIndex - 1);
714
134k
        if (nObjectOffsetBig < INT_MIN || nObjectOffsetBig > INT_MAX)
715
10.2k
            return nullptr;
716
124k
        nObjectOffset = static_cast<int>(nObjectOffsetBig);
717
124k
    }
718
719
    /*-----------------------------------------------------------------
720
     * Seek to the start of the object in the data file.
721
     *----------------------------------------------------------------*/
722
164k
    AVCRawBinFSeek(psFile->psRawBinFile, nObjectOffset, SEEK_SET);
723
164k
    if (AVCRawBinEOF(psFile->psRawBinFile))
724
29.1k
        return nullptr;
725
726
    /*-----------------------------------------------------------------
727
     * Read and return the object.
728
     *----------------------------------------------------------------*/
729
134k
    return AVCBinReadNextObject(psFile);
730
164k
}
731
732
/**********************************************************************
733
 *                          AVCBinReadNextObject()
734
 *
735
 * Read the next structure from the file.  This function is just a generic
736
 * cover on top of the AVCBinReadNextArc/Lab/Pal/Cnt() functions.
737
 *
738
 * Returns a (void*) to a static structure with the contents of the object
739
 * that was read.  The contents of the structure will be valid only until
740
 * the next call.
741
 * If you use the returned value, then make sure that you cast it to
742
 * the right type for the current file! (AVCArc, AVCPal, AVCCnt, ...)
743
 *
744
 * Returns nullptr if an error happened or if EOF was reached.
745
 **********************************************************************/
746
void *AVCBinReadNextObject(AVCBinFile *psFile)
747
317k
{
748
317k
    void *psObj = nullptr;
749
750
317k
    switch (psFile->eFileType)
751
317k
    {
752
41.1k
        case AVCFileARC:
753
41.1k
            psObj = (void *)AVCBinReadNextArc(psFile);
754
41.1k
            break;
755
4.22k
        case AVCFilePAL:
756
21.0k
        case AVCFileRPL:
757
21.0k
            psObj = (void *)AVCBinReadNextPal(psFile);
758
21.0k
            break;
759
3.81k
        case AVCFileCNT:
760
3.81k
            psObj = (void *)AVCBinReadNextCnt(psFile);
761
3.81k
            break;
762
148k
        case AVCFileLAB:
763
148k
            psObj = (void *)AVCBinReadNextLab(psFile);
764
148k
            break;
765
0
        case AVCFileTOL:
766
0
            psObj = (void *)AVCBinReadNextTol(psFile);
767
0
            break;
768
2.51k
        case AVCFileTXT:
769
7.11k
        case AVCFileTX6:
770
7.11k
            psObj = (void *)AVCBinReadNextTxt(psFile);
771
7.11k
            break;
772
0
        case AVCFileRXP:
773
0
            psObj = (void *)AVCBinReadNextRxp(psFile);
774
0
            break;
775
95.4k
        case AVCFileTABLE:
776
95.4k
            psObj = (void *)AVCBinReadNextTableRec(psFile);
777
95.4k
            break;
778
0
        default:
779
0
            CPLError(CE_Failure, CPLE_IllegalArg,
780
0
                     "AVCBinReadNextObject(): Unsupported file type!");
781
317k
    }
782
783
317k
    return psObj;
784
317k
}
785
786
/**********************************************************************
787
 *                          AVCBinReadNextTableRec()
788
 *
789
 * Reads the next record from an attribute table.
790
 *
791
 * Returns a pointer to an array of static AVCField structure whose
792
 * contents will be valid only until the next call,
793
 * or nullptr if an error happened or if EOF was reached.
794
 **********************************************************************/
795
AVCField *AVCBinReadNextTableRec(AVCBinFile *psFile)
796
95.4k
{
797
95.4k
    if (psFile->eCoverType != AVCCoverPC && psFile->eCoverType != AVCCoverPC2 &&
798
95.4k
        psFile->eFileType == AVCFileTABLE &&
799
95.4k
        psFile->hdr.psTableDef->numRecords > 0 &&
800
95.0k
        !AVCRawBinEOF(psFile->psRawBinFile) &&
801
95.0k
        _AVCBinReadNextTableRec(
802
95.0k
            psFile->psRawBinFile, psFile->hdr.psTableDef->numFields,
803
95.0k
            psFile->hdr.psTableDef->pasFieldDef, psFile->cur.pasFields,
804
95.0k
            psFile->hdr.psTableDef->nRecSize) == 0)
805
92.2k
    {
806
92.2k
        return psFile->cur.pasFields;
807
92.2k
    }
808
3.21k
    else if ((psFile->eCoverType == AVCCoverPC ||
809
3.21k
              psFile->eCoverType == AVCCoverPC2) &&
810
0
             psFile->eFileType == AVCFileTABLE &&
811
0
             psFile->hdr.psTableDef->numRecords > 0 &&
812
0
             _AVCBinReadNextDBFTableRec(psFile->hDBFFile,
813
0
                                        &(psFile->nCurDBFRecord),
814
0
                                        psFile->hdr.psTableDef->numFields,
815
0
                                        psFile->hdr.psTableDef->pasFieldDef,
816
0
                                        psFile->cur.pasFields) == 0)
817
0
    {
818
0
        return psFile->cur.pasFields;
819
0
    }
820
821
3.21k
    return nullptr;
822
95.4k
}
823
824
/*=====================================================================
825
 *                              ARC
826
 *====================================================================*/
827
828
/**********************************************************************
829
 *                          _AVCBinReadNextArc()
830
 *
831
 * (This function is for internal library use... external calls should
832
 * go to AVCBinReadNextArc() instead)
833
 *
834
 * Read the next Arc structure from the file.
835
 *
836
 * The contents of the psArc structure is assumed to be valid, and the
837
 * psArc->pasVertices buffer may be reallocated or free()'d if it is not
838
 * nullptr.
839
 *
840
 * Returns 0 on success or -1 on error.
841
 **********************************************************************/
842
static int _AVCBinReadNextArc(AVCRawBinFile *psFile, AVCArc *psArc,
843
                              int nPrecision)
844
41.0k
{
845
41.0k
    int i, numVertices;
846
41.0k
    int nRecordSize, nStartPos, nBytesRead;
847
848
41.0k
    psArc->nArcId = AVCRawBinReadInt32(psFile);
849
41.0k
    if (AVCRawBinEOF(psFile))
850
204
        return -1;
851
852
40.8k
    nRecordSize = AVCRawBinReadInt32(psFile);
853
40.8k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
854
931
        return -1;
855
39.9k
    nRecordSize *= 2;
856
39.9k
    nStartPos = psFile->nCurPos + psFile->nOffset;
857
39.9k
    psArc->nUserId = AVCRawBinReadInt32(psFile);
858
39.9k
    psArc->nFNode = AVCRawBinReadInt32(psFile);
859
39.9k
    psArc->nTNode = AVCRawBinReadInt32(psFile);
860
39.9k
    psArc->nLPoly = AVCRawBinReadInt32(psFile);
861
39.9k
    psArc->nRPoly = AVCRawBinReadInt32(psFile);
862
39.9k
    numVertices = AVCRawBinReadInt32(psFile);
863
39.9k
    if (numVertices < 0 || numVertices > 100 * 1024 * 1024)
864
616
        return -1;
865
39.3k
    if (numVertices > 10 * 1024 * 1024 &&
866
322
        !AVCRawBinIsFileGreaterThan(
867
322
            psFile,
868
322
            cpl::fits_on<int>(numVertices *
869
322
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
870
322
    {
871
322
        return -1;
872
322
    }
873
874
    /* Realloc the vertices array only if it needs to grow...
875
     * do not realloc to a smaller size.
876
     * Note that for simplicity reasons, we always store the vertices as
877
     * double values in memory, even for single precision coverages.
878
     */
879
38.9k
    if (psArc->pasVertices == nullptr || numVertices > psArc->numVertices)
880
2.68k
    {
881
2.68k
        AVCVertex *pasNewVertices = (AVCVertex *)VSIRealloc(
882
2.68k
            psArc->pasVertices, numVertices * sizeof(AVCVertex));
883
2.68k
        if (pasNewVertices == nullptr)
884
0
            return -1;
885
2.68k
        psArc->pasVertices = pasNewVertices;
886
2.68k
    }
887
888
38.9k
    psArc->numVertices = numVertices;
889
890
38.9k
    if (nPrecision == AVC_SINGLE_PREC)
891
12.9k
    {
892
478k
        for (i = 0; i < numVertices; i++)
893
465k
        {
894
465k
            psArc->pasVertices[i].x = AVCRawBinReadFloat(psFile);
895
465k
            psArc->pasVertices[i].y = AVCRawBinReadFloat(psFile);
896
465k
            if (psFile->nCurSize == 0)
897
238
                return -1;
898
465k
        }
899
12.9k
    }
900
26.0k
    else
901
26.0k
    {
902
451k
        for (i = 0; i < numVertices; i++)
903
425k
        {
904
425k
            psArc->pasVertices[i].x = AVCRawBinReadDouble(psFile);
905
425k
            psArc->pasVertices[i].y = AVCRawBinReadDouble(psFile);
906
425k
            if (psFile->nCurSize == 0)
907
354
                return -1;
908
425k
        }
909
26.0k
    }
910
911
    /*-----------------------------------------------------------------
912
     * Record size may be larger than number of vertices.  Skip up to
913
     * start of next object.
914
     *----------------------------------------------------------------*/
915
38.3k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
916
38.3k
    if (nBytesRead < nRecordSize)
917
5.00k
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
918
919
38.3k
    return 0;
920
38.9k
}
921
922
/**********************************************************************
923
 *                          AVCBinReadNextArc()
924
 *
925
 * Read the next Arc structure from the file.
926
 *
927
 * Returns a pointer to a static AVCArc structure whose contents will be
928
 * valid only until the next call or nullptr if an error happened or if EOF
929
 * was reached.
930
 **********************************************************************/
931
AVCArc *AVCBinReadNextArc(AVCBinFile *psFile)
932
41.1k
{
933
41.1k
    if (psFile->eFileType != AVCFileARC || AVCRawBinEOF(psFile->psRawBinFile) ||
934
41.0k
        _AVCBinReadNextArc(psFile->psRawBinFile, psFile->cur.psArc,
935
41.0k
                           psFile->nPrecision) != 0)
936
2.73k
    {
937
2.73k
        return nullptr;
938
2.73k
    }
939
940
38.3k
    return psFile->cur.psArc;
941
41.1k
}
942
943
/*=====================================================================
944
 *                              PAL
945
 *====================================================================*/
946
947
/**********************************************************************
948
 *                          _AVCBinReadNextPal()
949
 *
950
 * (This function is for internal library use... external calls should
951
 * go to AVCBinReadNextPal() instead)
952
 *
953
 * Read the next PAL (Polygon Arc List) structure from the file.
954
 *
955
 * The contents of the psPal structure is assumed to be valid, and the
956
 * psPal->paVertices buffer may be reallocated or free()'d if it is not
957
 * nullptr.
958
 *
959
 * Returns 0 on success or -1 on error.
960
 **********************************************************************/
961
static int _AVCBinReadNextPal(AVCRawBinFile *psFile, AVCPal *psPal,
962
                              int nPrecision)
963
19.7k
{
964
19.7k
    int i, numArcs;
965
19.7k
    int nRecordSize, nStartPos, nBytesRead;
966
967
19.7k
    psPal->nPolyId = AVCRawBinReadInt32(psFile);
968
19.7k
    nRecordSize = AVCRawBinReadInt32(psFile);
969
19.7k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
970
1.11k
        return -1;
971
18.6k
    nRecordSize *= 2;
972
18.6k
    nStartPos = psFile->nCurPos + psFile->nOffset;
973
974
18.6k
    if (AVCRawBinEOF(psFile))
975
42
        return -1;
976
977
18.5k
    if (nPrecision == AVC_SINGLE_PREC)
978
2.53k
    {
979
2.53k
        psPal->sMin.x = AVCRawBinReadFloat(psFile);
980
2.53k
        psPal->sMin.y = AVCRawBinReadFloat(psFile);
981
2.53k
        psPal->sMax.x = AVCRawBinReadFloat(psFile);
982
2.53k
        psPal->sMax.y = AVCRawBinReadFloat(psFile);
983
2.53k
    }
984
16.0k
    else
985
16.0k
    {
986
16.0k
        psPal->sMin.x = AVCRawBinReadDouble(psFile);
987
16.0k
        psPal->sMin.y = AVCRawBinReadDouble(psFile);
988
16.0k
        psPal->sMax.x = AVCRawBinReadDouble(psFile);
989
16.0k
        psPal->sMax.y = AVCRawBinReadDouble(psFile);
990
16.0k
    }
991
992
18.5k
    numArcs = AVCRawBinReadInt32(psFile);
993
18.5k
    if (numArcs < 0 || numArcs > 100 * 1024 * 1024)
994
547
        return -1;
995
18.0k
    if (numArcs > 10 * 1024 * 1024 &&
996
109
        !AVCRawBinIsFileGreaterThan(psFile, numArcs * sizeof(int) * 3))
997
109
    {
998
109
        return -1;
999
109
    }
1000
1001
    /* Realloc the arc list array only if it needs to grow...
1002
     * do not realloc to a smaller size.
1003
     */
1004
17.9k
    if (psPal->pasArcs == nullptr || numArcs > psPal->numArcs)
1005
6.24k
    {
1006
6.24k
        AVCPalArc *pasNewArcs = (AVCPalArc *)VSIRealloc(
1007
6.24k
            psPal->pasArcs, numArcs * sizeof(AVCPalArc));
1008
6.24k
        if (pasNewArcs == nullptr)
1009
0
            return -1;
1010
6.24k
        psPal->pasArcs = pasNewArcs;
1011
6.24k
    }
1012
1013
17.9k
    psPal->numArcs = numArcs;
1014
1015
149k
    for (i = 0; i < numArcs; i++)
1016
132k
    {
1017
132k
        psPal->pasArcs[i].nArcId = AVCRawBinReadInt32(psFile);
1018
132k
        psPal->pasArcs[i].nFNode = AVCRawBinReadInt32(psFile);
1019
132k
        psPal->pasArcs[i].nAdjPoly = AVCRawBinReadInt32(psFile);
1020
132k
        if (psFile->nCurSize == 0)
1021
220
            return -1;
1022
132k
    }
1023
1024
    /*-----------------------------------------------------------------
1025
     * Record size may be larger than number of vertices.  Skip up to
1026
     * start of next object.
1027
     *----------------------------------------------------------------*/
1028
17.7k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
1029
17.7k
    if (nBytesRead < nRecordSize)
1030
695
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
1031
1032
17.7k
    return 0;
1033
17.9k
}
1034
1035
/**********************************************************************
1036
 *                          AVCBinReadNextPal()
1037
 *
1038
 * Read the next PAL structure from the file.
1039
 *
1040
 * Returns a pointer to a static AVCPal structure whose contents will be
1041
 * valid only until the next call or nullptr if an error happened or if EOF
1042
 * was reached.
1043
 **********************************************************************/
1044
AVCPal *AVCBinReadNextPal(AVCBinFile *psFile)
1045
21.0k
{
1046
21.0k
    if ((psFile->eFileType != AVCFilePAL && psFile->eFileType != AVCFileRPL) ||
1047
21.0k
        AVCRawBinEOF(psFile->psRawBinFile) ||
1048
19.7k
        _AVCBinReadNextPal(psFile->psRawBinFile, psFile->cur.psPal,
1049
19.7k
                           psFile->nPrecision) != 0)
1050
3.37k
    {
1051
3.37k
        return nullptr;
1052
3.37k
    }
1053
1054
17.7k
    return psFile->cur.psPal;
1055
21.0k
}
1056
1057
/*=====================================================================
1058
 *                              CNT
1059
 *====================================================================*/
1060
1061
/**********************************************************************
1062
 *                          _AVCBinReadNextCnt()
1063
 *
1064
 * (This function is for internal library use... external calls should
1065
 * go to AVCBinReadNextCnt() instead)
1066
 *
1067
 * Read the next CNT (Polygon Centroid) structure from the file.
1068
 *
1069
 * Returns 0 on success or -1 on error.
1070
 **********************************************************************/
1071
static int _AVCBinReadNextCnt(AVCRawBinFile *psFile, AVCCnt *psCnt,
1072
                              int nPrecision)
1073
3.75k
{
1074
3.75k
    int i, numLabels;
1075
3.75k
    int nRecordSize, nStartPos, nBytesRead;
1076
1077
3.75k
    psCnt->nPolyId = AVCRawBinReadInt32(psFile);
1078
3.75k
    nRecordSize = AVCRawBinReadInt32(psFile);
1079
3.75k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1080
111
        return -1;
1081
3.63k
    nRecordSize *= 2;
1082
3.63k
    nStartPos = psFile->nCurPos + psFile->nOffset;
1083
1084
3.63k
    if (AVCRawBinEOF(psFile))
1085
5
        return -1;
1086
1087
3.63k
    if (nPrecision == AVC_SINGLE_PREC)
1088
1.49k
    {
1089
1.49k
        psCnt->sCoord.x = AVCRawBinReadFloat(psFile);
1090
1.49k
        psCnt->sCoord.y = AVCRawBinReadFloat(psFile);
1091
1.49k
    }
1092
2.14k
    else
1093
2.14k
    {
1094
2.14k
        psCnt->sCoord.x = AVCRawBinReadDouble(psFile);
1095
2.14k
        psCnt->sCoord.y = AVCRawBinReadDouble(psFile);
1096
2.14k
    }
1097
1098
3.63k
    numLabels = AVCRawBinReadInt32(psFile);
1099
3.63k
    if (numLabels < 0 || numLabels > 100 * 1024 * 1024)
1100
23
        return -1;
1101
3.61k
    if (numLabels > 10 * 1024 * 1024 &&
1102
10
        !AVCRawBinIsFileGreaterThan(psFile, numLabels * sizeof(int)))
1103
10
    {
1104
10
        return -1;
1105
10
    }
1106
1107
    /* Realloc the LabelIds array only if it needs to grow...
1108
     * do not realloc to a smaller size.
1109
     */
1110
3.60k
    if (psCnt->panLabelIds == nullptr || numLabels > psCnt->numLabels)
1111
266
    {
1112
266
        GInt32 *panIds = (GInt32 *)VSIRealloc(psCnt->panLabelIds,
1113
266
                                              numLabels * sizeof(GInt32));
1114
266
        if (panIds == nullptr)
1115
0
            return -1;
1116
266
        psCnt->panLabelIds = panIds;
1117
266
    }
1118
1119
3.60k
    psCnt->numLabels = numLabels;
1120
1121
91.8k
    for (i = 0; i < numLabels; i++)
1122
88.2k
    {
1123
88.2k
        psCnt->panLabelIds[i] = AVCRawBinReadInt32(psFile);
1124
88.2k
        if (psFile->nCurSize == 0)
1125
33
            return -1;
1126
88.2k
    }
1127
1128
    /*-----------------------------------------------------------------
1129
     * Record size may be larger than number of vertices.  Skip up to
1130
     * start of next object.
1131
     *----------------------------------------------------------------*/
1132
3.56k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
1133
3.56k
    if (nBytesRead < nRecordSize)
1134
325
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
1135
1136
3.56k
    return 0;
1137
3.60k
}
1138
1139
/**********************************************************************
1140
 *                          AVCBinReadNextCnt()
1141
 *
1142
 * Read the next CNT structure from the file.
1143
 *
1144
 * Returns a pointer to a static AVCCnt structure whose contents will be
1145
 * valid only until the next call or nullptr if an error happened or if EOF
1146
 * was reached.
1147
 **********************************************************************/
1148
AVCCnt *AVCBinReadNextCnt(AVCBinFile *psFile)
1149
3.81k
{
1150
3.81k
    if (psFile->eFileType != AVCFileCNT || AVCRawBinEOF(psFile->psRawBinFile) ||
1151
3.75k
        _AVCBinReadNextCnt(psFile->psRawBinFile, psFile->cur.psCnt,
1152
3.75k
                           psFile->nPrecision) != 0)
1153
250
    {
1154
250
        return nullptr;
1155
250
    }
1156
1157
3.56k
    return psFile->cur.psCnt;
1158
3.81k
}
1159
1160
/*=====================================================================
1161
 *                              LAB
1162
 *====================================================================*/
1163
1164
/**********************************************************************
1165
 *                          _AVCBinReadNextLab()
1166
 *
1167
 * (This function is for internal library use... external calls should
1168
 * go to AVCBinReadNextLab() instead)
1169
 *
1170
 * Read the next LAB (Centroid Label) structure from the file.
1171
 *
1172
 * Returns 0 on success or -1 on error.
1173
 **********************************************************************/
1174
static int _AVCBinReadNextLab(AVCRawBinFile *psFile, AVCLab *psLab,
1175
                              int nPrecision)
1176
147k
{
1177
1178
147k
    psLab->nValue = AVCRawBinReadInt32(psFile);
1179
147k
    psLab->nPolyId = AVCRawBinReadInt32(psFile);
1180
1181
147k
    if (AVCRawBinEOF(psFile))
1182
366
        return -1;
1183
1184
147k
    if (nPrecision == AVC_SINGLE_PREC)
1185
144k
    {
1186
144k
        psLab->sCoord1.x = AVCRawBinReadFloat(psFile);
1187
144k
        psLab->sCoord1.y = AVCRawBinReadFloat(psFile);
1188
144k
        psLab->sCoord2.x = AVCRawBinReadFloat(psFile);
1189
144k
        psLab->sCoord2.y = AVCRawBinReadFloat(psFile);
1190
144k
        psLab->sCoord3.x = AVCRawBinReadFloat(psFile);
1191
144k
        psLab->sCoord3.y = AVCRawBinReadFloat(psFile);
1192
144k
    }
1193
2.83k
    else
1194
2.83k
    {
1195
2.83k
        psLab->sCoord1.x = AVCRawBinReadDouble(psFile);
1196
2.83k
        psLab->sCoord1.y = AVCRawBinReadDouble(psFile);
1197
2.83k
        psLab->sCoord2.x = AVCRawBinReadDouble(psFile);
1198
2.83k
        psLab->sCoord2.y = AVCRawBinReadDouble(psFile);
1199
2.83k
        psLab->sCoord3.x = AVCRawBinReadDouble(psFile);
1200
2.83k
        psLab->sCoord3.y = AVCRawBinReadDouble(psFile);
1201
2.83k
    }
1202
1203
147k
    return 0;
1204
147k
}
1205
1206
/**********************************************************************
1207
 *                          AVCBinReadNextLab()
1208
 *
1209
 * Read the next LAB structure from the file.
1210
 *
1211
 * Returns a pointer to a static AVCLab structure whose contents will be
1212
 * valid only until the next call or nullptr if an error happened or if EOF
1213
 * was reached.
1214
 **********************************************************************/
1215
AVCLab *AVCBinReadNextLab(AVCBinFile *psFile)
1216
148k
{
1217
148k
    if (psFile->eFileType != AVCFileLAB || AVCRawBinEOF(psFile->psRawBinFile) ||
1218
147k
        _AVCBinReadNextLab(psFile->psRawBinFile, psFile->cur.psLab,
1219
147k
                           psFile->nPrecision) != 0)
1220
1.52k
    {
1221
1.52k
        return nullptr;
1222
1.52k
    }
1223
1224
147k
    return psFile->cur.psLab;
1225
148k
}
1226
1227
/*=====================================================================
1228
 *                              TOL
1229
 *====================================================================*/
1230
1231
/**********************************************************************
1232
 *                          _AVCBinReadNextTol()
1233
 *
1234
 * (This function is for internal library use... external calls should
1235
 * go to AVCBinReadNextTol() instead)
1236
 *
1237
 * Read the next TOL (tolerance) structure from the file.
1238
 *
1239
 * Returns 0 on success or -1 on error.
1240
 **********************************************************************/
1241
static int _AVCBinReadNextTol(AVCRawBinFile *psFile, AVCTol *psTol,
1242
                              int nPrecision)
1243
0
{
1244
1245
0
    psTol->nIndex = AVCRawBinReadInt32(psFile);
1246
0
    psTol->nFlag = AVCRawBinReadInt32(psFile);
1247
1248
0
    if (AVCRawBinEOF(psFile))
1249
0
        return -1;
1250
1251
0
    if (nPrecision == AVC_SINGLE_PREC)
1252
0
    {
1253
0
        psTol->dValue = AVCRawBinReadFloat(psFile);
1254
0
    }
1255
0
    else
1256
0
    {
1257
0
        psTol->dValue = AVCRawBinReadDouble(psFile);
1258
0
    }
1259
1260
0
    return 0;
1261
0
}
1262
1263
/**********************************************************************
1264
 *                          AVCBinReadNextTol()
1265
 *
1266
 * Read the next TOL structure from the file.
1267
 *
1268
 * Returns a pointer to a static AVCTol structure whose contents will be
1269
 * valid only until the next call or nullptr if an error happened or if EOF
1270
 * was reached.
1271
 **********************************************************************/
1272
AVCTol *AVCBinReadNextTol(AVCBinFile *psFile)
1273
0
{
1274
0
    if (psFile->eFileType != AVCFileTOL || AVCRawBinEOF(psFile->psRawBinFile) ||
1275
0
        _AVCBinReadNextTol(psFile->psRawBinFile, psFile->cur.psTol,
1276
0
                           psFile->nPrecision) != 0)
1277
0
    {
1278
0
        return nullptr;
1279
0
    }
1280
1281
0
    return psFile->cur.psTol;
1282
0
}
1283
1284
/*=====================================================================
1285
 *                              PRJ
1286
 *====================================================================*/
1287
1288
/**********************************************************************
1289
 *                          _AVCBinReadOpenPrj()
1290
 *
1291
 * (This function is for internal library use... external calls should
1292
 * go to AVCBinReadOpen() with type AVCFilePRJ instead)
1293
 *
1294
 * Open a PRJ file.
1295
 *
1296
 * This call will actually read the whole PRJ file in memory since PRJ
1297
 * files are small text files.
1298
 **********************************************************************/
1299
AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath, const char *pszName)
1300
12.0k
{
1301
12.0k
    AVCBinFile *psFile;
1302
12.0k
    char *pszFname, **papszPrj;
1303
1304
    /*-----------------------------------------------------------------
1305
     * Load the PRJ file contents into a stringlist.
1306
     *----------------------------------------------------------------*/
1307
12.0k
    pszFname = (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
1308
12.0k
    snprintf(pszFname, strlen(pszPath) + strlen(pszName) + 1, "%s%s", pszPath,
1309
12.0k
             pszName);
1310
1311
12.0k
    papszPrj = CSLLoad2(pszFname, 50, 160, nullptr);
1312
1313
12.0k
    CPLFree(pszFname);
1314
1315
12.0k
    if (papszPrj == nullptr)
1316
101
    {
1317
        /* Failed to open file... just return nullptr since an error message
1318
         * has already been issued by CSLLoad()
1319
         */
1320
101
        return nullptr;
1321
101
    }
1322
1323
    /*-----------------------------------------------------------------
1324
     * Alloc and init the AVCBinFile handle.
1325
     *----------------------------------------------------------------*/
1326
11.9k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
1327
1328
11.9k
    psFile->eFileType = AVCFilePRJ;
1329
11.9k
    psFile->psRawBinFile = nullptr;
1330
11.9k
    psFile->cur.papszPrj = papszPrj;
1331
11.9k
    psFile->pszFilename = nullptr;
1332
1333
11.9k
    return psFile;
1334
12.0k
}
1335
1336
/**********************************************************************
1337
 *                          AVCBinReadPrj()
1338
 *
1339
 * Return the contents of the previously opened PRJ (projection) file.
1340
 *
1341
 * PRJ files are simple text files with variable length lines, so we
1342
 * don't use the AVCRawBin*() functions for this case.
1343
 *
1344
 * Returns a reference to a static stringlist with the whole file
1345
 * contents, or nullptr in case of error.
1346
 *
1347
 * The returned stringlist should NOT be freed by the caller.
1348
 **********************************************************************/
1349
char **AVCBinReadNextPrj(AVCBinFile *psFile)
1350
11.9k
{
1351
    /*-----------------------------------------------------------------
1352
     * The file should have already been loaded by AVCBinFileOpen(),
1353
     * so there is not much to do here!
1354
     *----------------------------------------------------------------*/
1355
11.9k
    return psFile->cur.papszPrj;
1356
11.9k
}
1357
1358
/*=====================================================================
1359
 *                              TXT/TX6/TX7
1360
 *====================================================================*/
1361
1362
/**********************************************************************
1363
 *                          _AVCBinReadNextTxt()
1364
 *
1365
 * (This function is for internal library use... external calls should
1366
 * go to AVCBinReadNextTxt() instead)
1367
 *
1368
 * Read the next TXT/TX6/TX7 (Annotation) structure from the file.
1369
 *
1370
 * Returns 0 on success or -1 on error.
1371
 **********************************************************************/
1372
static int _AVCBinReadNextTxt(AVCRawBinFile *psFile, AVCTxt *psTxt,
1373
                              int nPrecision)
1374
6.10k
{
1375
6.10k
    int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1376
6.10k
    int numBytesRead;
1377
1378
6.10k
    numVerticesBefore =
1379
6.10k
        ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1380
1381
6.10k
    psTxt->nTxtId = AVCRawBinReadInt32(psFile);
1382
6.10k
    if (AVCRawBinEOF(psFile))
1383
32
        return -1;
1384
1385
6.07k
    nRecordSize = AVCRawBinReadInt32(psFile);
1386
6.07k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1387
366
        return -1;
1388
5.70k
    nRecordSize = nRecordSize * 2 + 8;
1389
1390
5.70k
    psTxt->nUserId = AVCRawBinReadInt32(psFile);
1391
5.70k
    psTxt->nLevel = AVCRawBinReadInt32(psFile);
1392
1393
5.70k
    psTxt->f_1e2 = AVCRawBinReadFloat(psFile);
1394
5.70k
    psTxt->nSymbol = AVCRawBinReadInt32(psFile);
1395
5.70k
    psTxt->numVerticesLine = AVCRawBinReadInt32(psFile);
1396
5.70k
    psTxt->n28 = AVCRawBinReadInt32(psFile);
1397
5.70k
    psTxt->numChars = AVCRawBinReadInt32(psFile);
1398
5.70k
    if (psTxt->numChars < 0 || psTxt->numChars > 10 * 1024 * 1024)
1399
115
        return -1;
1400
5.59k
    psTxt->numVerticesArrow = AVCRawBinReadInt32(psFile);
1401
1402
117k
    for (i = 0; i < 20; i++)
1403
111k
    {
1404
111k
        psTxt->anJust1[i] = AVCRawBinReadInt16(psFile);
1405
111k
    }
1406
117k
    for (i = 0; i < 20; i++)
1407
111k
    {
1408
111k
        psTxt->anJust2[i] = AVCRawBinReadInt16(psFile);
1409
111k
    }
1410
1411
5.59k
    if (nPrecision == AVC_SINGLE_PREC)
1412
5.07k
    {
1413
5.07k
        psTxt->dHeight = AVCRawBinReadFloat(psFile);
1414
5.07k
        psTxt->dV2 = AVCRawBinReadFloat(psFile);
1415
5.07k
        psTxt->dV3 = AVCRawBinReadFloat(psFile);
1416
5.07k
    }
1417
522
    else
1418
522
    {
1419
522
        psTxt->dHeight = AVCRawBinReadDouble(psFile);
1420
522
        psTxt->dV2 = AVCRawBinReadDouble(psFile);
1421
522
        psTxt->dV3 = AVCRawBinReadDouble(psFile);
1422
522
    }
1423
1424
5.59k
    numCharsToRead = ((int)(psTxt->numChars + 3) / 4) * 4;
1425
5.59k
    if (psTxt->pszText == nullptr ||
1426
5.02k
        ((int)(strlen((char *)psTxt->pszText) + 3) / 4) * 4 < numCharsToRead)
1427
853
    {
1428
853
        GByte *pszNewText = (GByte *)VSIRealloc(
1429
853
            psTxt->pszText, (numCharsToRead + 1) * sizeof(char));
1430
853
        if (pszNewText == nullptr)
1431
0
            return -1;
1432
853
        psTxt->pszText = pszNewText;
1433
853
    }
1434
1435
5.59k
    AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1436
5.59k
    psTxt->pszText[psTxt->numChars] = '\0';
1437
1438
    /* Realloc the vertices array only if it needs to grow...
1439
     * do not realloc to a smaller size.
1440
     */
1441
5.59k
    if (psTxt->numVerticesLine == INT_MIN ||
1442
5.59k
        psTxt->numVerticesArrow == INT_MIN ||
1443
5.55k
        ABS(psTxt->numVerticesLine) >
1444
5.55k
            100 * 1024 * 1024 - ABS(psTxt->numVerticesArrow))
1445
125
        return -1;
1446
5.46k
    numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1447
5.46k
    if (numVertices > 10 * 1024 * 1024 &&
1448
30
        !AVCRawBinIsFileGreaterThan(
1449
30
            psFile,
1450
30
            cpl::fits_on<int>(numVertices *
1451
30
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
1452
30
    {
1453
30
        return -1;
1454
30
    }
1455
1456
5.43k
    if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1457
2.54k
        psTxt->pasVertices = (AVCVertex *)CPLRealloc(
1458
2.54k
            psTxt->pasVertices, numVertices * sizeof(AVCVertex));
1459
1460
5.43k
    if (nPrecision == AVC_SINGLE_PREC)
1461
4.99k
    {
1462
92.8k
        for (i = 0; i < numVertices; i++)
1463
87.9k
        {
1464
87.9k
            psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1465
87.9k
            psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1466
87.9k
            if (psFile->nCurSize == 0)
1467
104
                return -1;
1468
87.9k
        }
1469
4.99k
    }
1470
440
    else
1471
440
    {
1472
5.99k
        for (i = 0; i < numVertices; i++)
1473
5.59k
        {
1474
5.59k
            psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1475
5.59k
            psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1476
5.59k
            if (psFile->nCurSize == 0)
1477
38
                return -1;
1478
5.59k
        }
1479
440
    }
1480
1481
    /* In V7 Coverages, we always have 8 bytes of junk at end of record.
1482
     * In Weird coverages, these 8 bytes are sometimes present, and
1483
     * sometimes not!!! (Probably another AI "random feature"! ;-)
1484
     * So we use the record size to establish if there is any junk to skip
1485
     */
1486
5.29k
    if (nPrecision == AVC_SINGLE_PREC)
1487
4.89k
        numBytesRead = 132 + numCharsToRead + numVertices * 2 * 4;
1488
402
    else
1489
402
        numBytesRead = 144 + numCharsToRead + numVertices * 2 * 8;
1490
1491
5.29k
    if (numBytesRead < nRecordSize)
1492
176
        AVCRawBinFSeek(psFile, nRecordSize - numBytesRead, SEEK_CUR);
1493
1494
5.29k
    return 0;
1495
5.43k
}
1496
1497
/**********************************************************************
1498
 *                          _AVCBinReadNextPCCoverageTxt()
1499
 *
1500
 * (This function is for internal library use... external calls should
1501
 * go to AVCBinReadNextTxt() instead)
1502
 *
1503
 * Read the next TXT (Annotation) structure from a PC Coverage file.
1504
 * Note that it is assumed that PC Coverage files are always single
1505
 * precision.
1506
 *
1507
 * Returns 0 on success or -1 on error.
1508
 **********************************************************************/
1509
static int _AVCBinReadNextPCCoverageTxt(AVCRawBinFile *psFile, AVCTxt *psTxt,
1510
                                        int nPrecision)
1511
326
{
1512
326
    int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1513
1514
326
    numVerticesBefore =
1515
326
        ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1516
1517
326
    psTxt->nTxtId = AVCRawBinReadInt32(psFile);
1518
326
    if (AVCRawBinEOF(psFile))
1519
2
        return -1;
1520
1521
324
    nRecordSize = AVCRawBinReadInt32(psFile);
1522
324
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1523
42
        return -1;
1524
282
    nRecordSize = nRecordSize * 2 + 8;
1525
1526
282
    psTxt->nUserId = 0;
1527
282
    psTxt->nLevel = AVCRawBinReadInt32(psFile);
1528
1529
282
    psTxt->numVerticesLine = AVCRawBinReadInt32(psFile);
1530
    /* We are not expecting more than 4 vertices */
1531
282
    psTxt->numVerticesLine = MIN(psTxt->numVerticesLine, 4);
1532
1533
282
    psTxt->numVerticesArrow = 0;
1534
1535
    /* Realloc the vertices array only if it needs to grow...
1536
     * do not realloc to a smaller size.
1537
     *
1538
     * Note that because of the way V7 binary TXT files work, the rest of the
1539
     * lib expects to receive duplicate coords for the first vertex, so
1540
     * we have to include an additional vertex for that.
1541
     */
1542
282
    psTxt->numVerticesLine += 1;
1543
282
    if (psTxt->numVerticesLine == INT_MIN ||
1544
282
        ABS(psTxt->numVerticesLine) > 100 * 1024 * 1024)
1545
8
        return -1;
1546
274
    numVertices = ABS(psTxt->numVerticesLine);
1547
274
    if (numVertices < 2)
1548
4
        return -1;
1549
270
    if (numVertices > 10 * 1024 * 1024 &&
1550
11
        !AVCRawBinIsFileGreaterThan(
1551
11
            psFile,
1552
11
            cpl::fits_on<int>(numVertices *
1553
11
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
1554
11
    {
1555
11
        return -1;
1556
11
    }
1557
1558
259
    if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1559
154
        psTxt->pasVertices = (AVCVertex *)CPLRealloc(
1560
154
            psTxt->pasVertices, numVertices * sizeof(AVCVertex));
1561
1562
45.4k
    for (i = 1; i < numVertices; i++)
1563
45.2k
    {
1564
45.2k
        if (nPrecision == AVC_SINGLE_PREC)
1565
44.8k
        {
1566
44.8k
            psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1567
44.8k
            psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1568
44.8k
            if (psFile->nCurSize == 0)
1569
15
                return -1;
1570
44.8k
        }
1571
347
        else
1572
347
        {
1573
347
            psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1574
347
            psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1575
347
            if (psFile->nCurSize == 0)
1576
6
                return -1;
1577
347
        }
1578
45.2k
    }
1579
    /* Duplicate the first vertex because that's the way the other binary TXT
1580
     * files work and that's what the lib expects to generate the E00.
1581
     */
1582
238
    psTxt->pasVertices[0].x = psTxt->pasVertices[1].x;
1583
238
    psTxt->pasVertices[0].y = psTxt->pasVertices[1].y;
1584
1585
    /* Skip the other floats (vertices) that are unused */
1586
238
    if (nPrecision == AVC_SINGLE_PREC)
1587
207
        AVCRawBinFSeek(psFile, 4 * (15 - 2 * (numVertices - 1)), SEEK_CUR);
1588
31
    else
1589
31
        AVCRawBinFSeek(psFile, 8 * (15 - 2 * (numVertices - 1)), SEEK_CUR);
1590
1591
238
    if (nPrecision == AVC_SINGLE_PREC)
1592
207
    {
1593
207
        psTxt->dHeight = AVCRawBinReadFloat(psFile);
1594
207
    }
1595
31
    else
1596
31
    {
1597
31
        psTxt->dHeight = AVCRawBinReadDouble(psFile);
1598
31
    }
1599
238
    psTxt->f_1e2 = AVCRawBinReadFloat(psFile);
1600
238
    psTxt->nSymbol = AVCRawBinReadInt32(psFile);
1601
238
    psTxt->numChars = AVCRawBinReadInt32(psFile);
1602
238
    if (psTxt->numChars < 0)
1603
3
        return -1;
1604
1605
    /* In some cases, we may need to skip additional spaces after the
1606
     * text string... more than should be required to simply align with
1607
     * a 4 bytes boundary... include that in numCharsToRead
1608
     */
1609
235
    if (nPrecision == AVC_SINGLE_PREC)
1610
205
    {
1611
205
        numCharsToRead = nRecordSize - (28 + 16 * 4);
1612
205
    }
1613
30
    else
1614
30
    {
1615
30
        numCharsToRead = nRecordSize - (28 + 16 * 8);
1616
30
    }
1617
235
    if (numCharsToRead < 0)
1618
8
        return -1;
1619
1620
    /* Do a quick check in case file is corrupt! */
1621
227
    psTxt->numChars = MIN(psTxt->numChars, numCharsToRead);
1622
1623
227
    if (psTxt->pszText == nullptr ||
1624
119
        ((int)(strlen((char *)psTxt->pszText) + 3) / 4) * 4 < numCharsToRead)
1625
203
    {
1626
203
        psTxt->pszText = (GByte *)CPLRealloc(
1627
203
            psTxt->pszText, (numCharsToRead + 5) * sizeof(char));
1628
203
    }
1629
1630
227
    AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1631
227
    psTxt->pszText[psTxt->numChars] = '\0';
1632
1633
    /* Set unused members to default values...
1634
     */
1635
227
    psTxt->dV2 = 0.0;
1636
227
    psTxt->dV3 = 0.0;
1637
227
    psTxt->n28 = 0;
1638
4.76k
    for (i = 0; i < 20; i++)
1639
4.54k
    {
1640
4.54k
        psTxt->anJust1[i] = 0;
1641
4.54k
        psTxt->anJust2[i] = 0;
1642
4.54k
    }
1643
1644
227
    return 0;
1645
235
}
1646
1647
/**********************************************************************
1648
 *                          AVCBinReadNextTxt()
1649
 *
1650
 * Read the next TXT/TX6/TX7 structure from the file.
1651
 *
1652
 * Returns a pointer to a static AVCTxt structure whose contents will be
1653
 * valid only until the next call or nullptr if an error happened or if EOF
1654
 * was reached.
1655
 **********************************************************************/
1656
AVCTxt *AVCBinReadNextTxt(AVCBinFile *psFile)
1657
7.11k
{
1658
7.11k
    int nStatus = 0;
1659
1660
7.11k
    if ((psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6) ||
1661
7.11k
        AVCRawBinEOF(psFile->psRawBinFile))
1662
679
    {
1663
679
        return nullptr;
1664
679
    }
1665
1666
    /* AVCCoverPC have a different TXT format than AVCCoverV7
1667
     *
1668
     * Note: Some Weird coverages use the PC TXT structure, and some use the
1669
     *       V7 structure.  We distinguish them using the header's precision
1670
     *       field in AVCBinReadRewind().
1671
     */
1672
6.43k
    if (psFile->eFileType == AVCFileTXT &&
1673
2.42k
        (psFile->eCoverType == AVCCoverPC ||
1674
2.14k
         psFile->eCoverType == AVCCoverWeird))
1675
326
    {
1676
        /* TXT file in PC Coverages (and some Weird Coverages)
1677
         */
1678
326
        nStatus = _AVCBinReadNextPCCoverageTxt(
1679
326
            psFile->psRawBinFile, psFile->cur.psTxt, psFile->nPrecision);
1680
326
    }
1681
6.10k
    else
1682
6.10k
    {
1683
        /* TXT in V7 Coverages (and some Weird Coverages), and TX6/TX7 in
1684
         * all coverage types
1685
         */
1686
6.10k
        nStatus = _AVCBinReadNextTxt(psFile->psRawBinFile, psFile->cur.psTxt,
1687
6.10k
                                     psFile->nPrecision);
1688
6.10k
    }
1689
1690
6.43k
    if (nStatus != 0)
1691
909
    {
1692
909
        return nullptr;
1693
909
    }
1694
1695
5.52k
    return psFile->cur.psTxt;
1696
6.43k
}
1697
1698
/*=====================================================================
1699
 *                              RXP
1700
 *====================================================================*/
1701
1702
/**********************************************************************
1703
 *                          _AVCBinReadNextRxp()
1704
 *
1705
 * (This function is for internal library use... external calls should
1706
 * go to AVCBinReadNextRxp() instead)
1707
 *
1708
 * Read the next RXP (Region something...) structure from the file.
1709
 *
1710
 * Returns 0 on success or -1 on error.
1711
 **********************************************************************/
1712
static int _AVCBinReadNextRxp(AVCRawBinFile *psFile, AVCRxp *psRxp,
1713
                              CPL_UNUSED int nPrecision)
1714
0
{
1715
1716
0
    psRxp->n1 = AVCRawBinReadInt32(psFile);
1717
0
    if (AVCRawBinEOF(psFile))
1718
0
        return -1;
1719
0
    psRxp->n2 = AVCRawBinReadInt32(psFile);
1720
1721
0
    return 0;
1722
0
}
1723
1724
/**********************************************************************
1725
 *                          AVCBinReadNextRxp()
1726
 *
1727
 * Read the next RXP structure from the file.
1728
 *
1729
 * Returns a pointer to a static AVCRxp structure whose contents will be
1730
 * valid only until the next call or nullptr if an error happened or if EOF
1731
 * was reached.
1732
 **********************************************************************/
1733
AVCRxp *AVCBinReadNextRxp(AVCBinFile *psFile)
1734
0
{
1735
0
    if (psFile->eFileType != AVCFileRXP || AVCRawBinEOF(psFile->psRawBinFile) ||
1736
0
        _AVCBinReadNextRxp(psFile->psRawBinFile, psFile->cur.psRxp,
1737
0
                           psFile->nPrecision) != 0)
1738
0
    {
1739
0
        return nullptr;
1740
0
    }
1741
1742
0
    return psFile->cur.psRxp;
1743
0
}
1744
1745
/*=====================================================================
1746
 *                         NATIVE (V7.x) TABLEs
1747
 *
1748
 * Note: Also applies to AVCCoverWeird
1749
 *====================================================================*/
1750
1751
/**********************************************************************
1752
 *                          _AVCBinReadNextArcDir()
1753
 *
1754
 * (This function is for internal library use... external calls should
1755
 * go to AVCBinReadOpen() with type AVCFileTABLE instead)
1756
 *
1757
 * Read the next record from an arc.dir (or "arcdr9") file.
1758
 *
1759
 * Note that arc.dir files have no header... they start with the
1760
 * first record immediately.
1761
 *
1762
 * Returns 0 on success or -1 on error.
1763
 **********************************************************************/
1764
1765
int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir)
1766
103k
{
1767
103k
    int i;
1768
1769
    /* Arc/Info Table name
1770
     */
1771
103k
    AVCRawBinReadString(psFile, 32, (GByte *)psArcDir->szTableName);
1772
103k
    psArcDir->szTableName[32] = '\0';
1773
1774
103k
    if (AVCRawBinEOF(psFile))
1775
468
        return -1;
1776
1777
    /* "ARC####" basename for .DAT and .NIT files
1778
     */
1779
103k
    AVCRawBinReadString(psFile, 8, (GByte *)psArcDir->szInfoFile);
1780
103k
    psArcDir->szInfoFile[7] = '\0';
1781
123k
    for (i = 6; i > 0 && psArcDir->szInfoFile[i] == ' '; i--)
1782
19.6k
        psArcDir->szInfoFile[i] = '\0';
1783
1784
103k
    psArcDir->numFields = AVCRawBinReadInt16(psFile);
1785
103k
    psArcDir->nRecSize = AVCRawBinReadInt16(psFile);
1786
1787
103k
    AVCRawBinFSeek(psFile, 18, SEEK_CUR); /* Skip 18 bytes */
1788
1789
103k
    psArcDir->bDeletedFlag = AVCRawBinReadInt16(psFile);
1790
103k
    psArcDir->numRecords = AVCRawBinReadInt32(psFile);
1791
1792
103k
    AVCRawBinFSeek(psFile, 10, SEEK_CUR); /* Skip 10 bytes */
1793
1794
103k
    AVCRawBinReadBytes(psFile, 2, (GByte *)psArcDir->szExternal);
1795
103k
    psArcDir->szExternal[2] = '\0';
1796
1797
103k
    AVCRawBinFSeek(psFile, 300, SEEK_CUR); /* Skip the remaining 300 bytes */
1798
1799
103k
    return 0;
1800
103k
}
1801
1802
/**********************************************************************
1803
 *                          _AVCBinReadNextNit()
1804
 *
1805
 * (This function is for internal library use... external calls should
1806
 * go to AVCBinReadOpen() with type AVCFileTABLE instead)
1807
 *
1808
 * Read the next record from an arc####.nit file.
1809
 *
1810
 * Note that arc####.nit files have no header... they start with the
1811
 * first record immediately.
1812
 *
1813
 * Returns 0 on success or -1 on error.
1814
 **********************************************************************/
1815
static int _AVCBinReadNextArcNit(AVCRawBinFile *psFile, AVCFieldInfo *psField)
1816
20.1k
{
1817
20.1k
    AVCRawBinReadString(psFile, 16, (GByte *)psField->szName);
1818
20.1k
    psField->szName[16] = '\0';
1819
1820
20.1k
    if (AVCRawBinEOF(psFile))
1821
61
        return -1;
1822
1823
20.0k
    psField->nSize = AVCRawBinReadInt16(psFile);
1824
20.0k
    if (psField->nSize < 0)
1825
57
        return -1;
1826
20.0k
    psField->v2 = AVCRawBinReadInt16(psFile); /* Always -1 ? */
1827
20.0k
    psField->nOffset = AVCRawBinReadInt16(psFile);
1828
20.0k
    psField->v4 = AVCRawBinReadInt16(psFile); /* Always 4 ?  */
1829
20.0k
    psField->v5 = AVCRawBinReadInt16(psFile); /* Always -1 ? */
1830
20.0k
    psField->nFmtWidth = AVCRawBinReadInt16(psFile);
1831
20.0k
    psField->nFmtPrec = AVCRawBinReadInt16(psFile);
1832
20.0k
    psField->nType1 = AVCRawBinReadInt16(psFile);
1833
20.0k
    psField->nType2 = AVCRawBinReadInt16(psFile); /* Always 0 ? */
1834
20.0k
    psField->v10 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1835
20.0k
    psField->v11 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1836
20.0k
    psField->v12 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1837
20.0k
    psField->v13 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1838
1839
20.0k
    AVCRawBinReadString(psFile, 16,
1840
20.0k
                        (GByte *)psField->szAltName); /* Always Blank ? */
1841
20.0k
    psField->szAltName[16] = '\0';
1842
1843
20.0k
    AVCRawBinFSeek(psFile, 56, SEEK_CUR); /* Skip 56 bytes */
1844
1845
20.0k
    psField->nIndex = AVCRawBinReadInt16(psFile);
1846
1847
20.0k
    AVCRawBinFSeek(psFile, 28, SEEK_CUR); /* Skip the remaining 28 bytes */
1848
1849
20.0k
    return 0;
1850
20.0k
}
1851
1852
/**********************************************************************
1853
 *                          _AVCBinReadGetInfoFilename()
1854
 *
1855
 * Look for the DAT or NIT files for a given table... returns TRUE if
1856
 * they exist, or FALSE otherwise.
1857
 *
1858
 * If pszRetFnmae/pszRetNitFile != nullptr then the filename with full path
1859
 * will be copied to the specified buffer.
1860
 **********************************************************************/
1861
static GBool _AVCBinReadGetInfoFilename(const char *pszInfoPath,
1862
                                        const char *pszBasename,
1863
                                        const char *pszDatOrNit,
1864
                                        AVCCoverType eCoverType,
1865
                                        char *pszRetFname, size_t nRetFnameLen)
1866
54.8k
{
1867
54.8k
    GBool bFilesExist = FALSE;
1868
54.8k
    char *pszBuf = nullptr;
1869
54.8k
    VSIStatBufL sStatBuf;
1870
54.8k
    size_t nBufLen;
1871
1872
54.8k
    if (pszRetFname)
1873
9.19k
    {
1874
9.19k
        pszBuf = pszRetFname;
1875
9.19k
        nBufLen = nRetFnameLen;
1876
9.19k
    }
1877
45.7k
    else
1878
45.7k
    {
1879
45.7k
        nBufLen = strlen(pszInfoPath) + strlen(pszBasename) + 10;
1880
45.7k
        pszBuf = (char *)CPLMalloc(nBufLen);
1881
45.7k
    }
1882
1883
54.8k
    if (eCoverType == AVCCoverWeird)
1884
239
    {
1885
239
        snprintf(pszBuf, nBufLen, "%s%s%s", pszInfoPath, pszBasename,
1886
239
                 pszDatOrNit);
1887
239
    }
1888
54.6k
    else
1889
54.6k
    {
1890
54.6k
        snprintf(pszBuf, nBufLen, "%s%s.%s", pszInfoPath, pszBasename,
1891
54.6k
                 pszDatOrNit);
1892
54.6k
    }
1893
1894
54.8k
    AVCAdjustCaseSensitiveFilename(pszBuf);
1895
1896
54.8k
    if (VSIStatL(pszBuf, &sStatBuf) == 0)
1897
37.2k
        bFilesExist = TRUE;
1898
1899
54.8k
    if (eCoverType == AVCCoverWeird && !bFilesExist)
1900
100
    {
1901
        /* In some cases, the filename can be truncated to 8 chars
1902
         * and we end up with "ARC000DA"... check that possibility.
1903
         */
1904
100
        pszBuf[strlen(pszBuf) - 1] = '\0';
1905
1906
100
        AVCAdjustCaseSensitiveFilename(pszBuf);
1907
1908
100
        if (VSIStatL(pszBuf, &sStatBuf) == 0)
1909
79
            bFilesExist = TRUE;
1910
100
    }
1911
1912
54.8k
    if (pszRetFname == nullptr)
1913
45.7k
        CPLFree(pszBuf);
1914
1915
54.8k
    return bFilesExist;
1916
54.8k
}
1917
1918
/**********************************************************************
1919
 *                          _AVCBinReadInfoFilesExist()
1920
 *
1921
 * Look for the DAT and NIT files for a given table... returns TRUE if
1922
 * they exist, or FALSE otherwise.
1923
 *
1924
 * If pszRetDatFile/pszRetNitFile != nullptr then the .DAT and .NIT filename
1925
 * without the info path will be copied to the specified buffers.
1926
 **********************************************************************/
1927
static GBool _AVCBinReadInfoFileExists(const char *pszInfoPath,
1928
                                       const char *pszBasename,
1929
                                       AVCCoverType eCoverType)
1930
31.2k
{
1931
1932
31.2k
    return (_AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, "dat",
1933
31.2k
                                       eCoverType, nullptr, 0) == TRUE &&
1934
14.4k
            _AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, "nit",
1935
14.4k
                                       eCoverType, nullptr, 0) == TRUE);
1936
31.2k
}
1937
1938
/**********************************************************************
1939
 *                          AVCBinReadListTables()
1940
 *
1941
 * Scan the arc.dir file and return stringlist with one entry for the
1942
 * Arc/Info name of each table that belongs to the specified coverage.
1943
 * Pass pszCoverName = nullptr to get the list of all tables.
1944
 *
1945
 * ppapszArcDatFiles if not nullptr will be set to point to a stringlist
1946
 * with the corresponding "ARC????" info file basenames corresponding
1947
 * to each table found.
1948
 *
1949
 * Note that arc.dir files have no header... they start with the
1950
 * first record immediately.
1951
 *
1952
 * In AVCCoverWeird, the file is called "arcdr9"
1953
 *
1954
 * Returns a stringlist that should be deallocated by the caller
1955
 * with CSLDestroy(), or nullptr on error.
1956
 **********************************************************************/
1957
char **AVCBinReadListTables(const char *pszInfoPath, const char *pszCoverName,
1958
                            char ***ppapszArcDatFiles, AVCCoverType eCoverType,
1959
                            AVCDBCSInfo *psDBCSInfo)
1960
9.19k
{
1961
9.19k
    char **papszList = nullptr;
1962
9.19k
    char *pszFname;
1963
9.19k
    char szNameToFind[33] = "";
1964
9.19k
    int nLen;
1965
9.19k
    AVCRawBinFile *hFile;
1966
9.19k
    AVCTableDef sEntry;
1967
1968
9.19k
    if (ppapszArcDatFiles)
1969
9.19k
        *ppapszArcDatFiles = nullptr;
1970
1971
    /*-----------------------------------------------------------------
1972
     * For AVCCoverV7Tables type we do not look for tables for a specific
1973
     * coverage, we return all tables from the info dir.
1974
     *----------------------------------------------------------------*/
1975
9.19k
    if (eCoverType == AVCCoverV7Tables)
1976
230
        pszCoverName = nullptr;
1977
1978
    /*-----------------------------------------------------------------
1979
     * All tables that belong to a given coverage have their name starting
1980
     * with the coverage name (in uppercase letters), followed by a 3
1981
     * letters extension.
1982
     *----------------------------------------------------------------*/
1983
9.19k
    if (pszCoverName != nullptr)
1984
        // cppcheck-suppress bufferAccessOutOfBounds
1985
8.96k
        snprintf(szNameToFind, sizeof(szNameToFind), "%-.28s.", pszCoverName);
1986
9.19k
    nLen = (int)strlen(szNameToFind);
1987
1988
    /*-----------------------------------------------------------------
1989
     * Open the arc.dir and add all entries that match the criteria
1990
     * to our list.
1991
     * In AVCCoverWeird, the file is called "arcdr9"
1992
     *----------------------------------------------------------------*/
1993
9.19k
    pszFname = (char *)CPLMalloc(strlen(pszInfoPath) + 9);
1994
9.19k
    if (eCoverType == AVCCoverWeird)
1995
55
        snprintf(pszFname, strlen(pszInfoPath) + 9, "%sarcdr9", pszInfoPath);
1996
9.14k
    else
1997
9.14k
        snprintf(pszFname, strlen(pszInfoPath) + 9, "%sarc.dir", pszInfoPath);
1998
1999
9.19k
    AVCAdjustCaseSensitiveFilename(pszFname);
2000
2001
9.19k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2002
9.19k
                          psDBCSInfo);
2003
2004
9.19k
    if (hFile)
2005
9.19k
    {
2006
91.5k
        while (!AVCRawBinEOF(hFile) &&
2007
82.8k
               _AVCBinReadNextArcDir(hFile, &sEntry) == 0)
2008
82.3k
        {
2009
82.3k
            if (/* sEntry.numRecords > 0 && (DO NOT skip empty tables) */
2010
82.3k
                !sEntry.bDeletedFlag &&
2011
40.3k
                (pszCoverName == nullptr ||
2012
15.5k
                 EQUALN(szNameToFind, sEntry.szTableName, nLen)) &&
2013
26.5k
                _AVCBinReadInfoFileExists(pszInfoPath, sEntry.szInfoFile,
2014
26.5k
                                          eCoverType))
2015
8.98k
            {
2016
8.98k
                papszList = CSLAddString(papszList, sEntry.szTableName);
2017
2018
8.98k
                if (ppapszArcDatFiles)
2019
8.98k
                    *ppapszArcDatFiles =
2020
8.98k
                        CSLAddString(*ppapszArcDatFiles, sEntry.szInfoFile);
2021
8.98k
            }
2022
82.3k
        }
2023
9.19k
        AVCRawBinClose(hFile);
2024
9.19k
    }
2025
2026
9.19k
    CPLFree(pszFname);
2027
2028
9.19k
    return papszList;
2029
9.19k
}
2030
2031
/**********************************************************************
2032
 *                         _AVCBinReadOpenTable()
2033
 *
2034
 * (This function is for internal library use... external calls should
2035
 * go to AVCBinReadOpen() with type AVCFileTABLE instead)
2036
 *
2037
 * Open a INFO table, read the header file (.NIT), and finally open
2038
 * the associated data file to be ready to read records from it.
2039
 *
2040
 * Returns a valid AVCBinFile handle, or nullptr if the file could
2041
 * not be opened.
2042
 *
2043
 * _AVCBinReadCloseTable() will eventually have to be called to release the
2044
 * resources used by the AVCBinFile structure.
2045
 **********************************************************************/
2046
AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath,
2047
                                 const char *pszTableName,
2048
                                 AVCCoverType eCoverType,
2049
                                 AVCDBCSInfo *psDBCSInfo)
2050
4.68k
{
2051
4.68k
    AVCBinFile *psFile;
2052
4.68k
    AVCRawBinFile *hFile;
2053
4.68k
    AVCTableDef sTableDef;
2054
4.68k
    AVCFieldInfo *pasFieldDef;
2055
4.68k
    char *pszFname;
2056
4.68k
    GBool bFound;
2057
4.68k
    int i;
2058
4.68k
    size_t nFnameLen;
2059
2060
4.68k
    memset(&sTableDef, 0, sizeof(sTableDef));
2061
4.68k
    sTableDef.numFields = 0;
2062
4.68k
    sTableDef.pasFieldDef = nullptr;
2063
2064
    /* Alloc a buffer big enough for the longest possible filename...
2065
     */
2066
4.68k
    nFnameLen = strlen(pszInfoPath) + 81;
2067
4.68k
    pszFname = (char *)CPLMalloc(nFnameLen);
2068
2069
    /*-----------------------------------------------------------------
2070
     * Fetch info about this table from the "arc.dir"
2071
     *----------------------------------------------------------------*/
2072
4.68k
    if (eCoverType == AVCCoverWeird)
2073
17
        snprintf(pszFname, nFnameLen, "%sarcdr9", pszInfoPath);
2074
4.66k
    else
2075
4.66k
        snprintf(pszFname, nFnameLen, "%sarc.dir", pszInfoPath);
2076
2077
4.68k
    AVCAdjustCaseSensitiveFilename(pszFname);
2078
2079
4.68k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2080
4.68k
                          psDBCSInfo);
2081
4.68k
    bFound = FALSE;
2082
2083
4.68k
    if (hFile)
2084
4.68k
    {
2085
25.7k
        while (!bFound && _AVCBinReadNextArcDir(hFile, &sTableDef) == 0)
2086
21.1k
        {
2087
21.1k
            if (!sTableDef.bDeletedFlag &&
2088
12.2k
                EQUALN(sTableDef.szTableName, pszTableName,
2089
21.1k
                       strlen(pszTableName)) &&
2090
4.71k
                _AVCBinReadInfoFileExists(pszInfoPath, sTableDef.szInfoFile,
2091
4.71k
                                          eCoverType))
2092
4.66k
            {
2093
4.66k
                bFound = TRUE;
2094
4.66k
            }
2095
21.1k
        }
2096
4.68k
        AVCRawBinClose(hFile);
2097
4.68k
    }
2098
2099
    /* Hummm... quite likely that this table does not exist!
2100
     */
2101
4.68k
    if (!bFound)
2102
25
    {
2103
25
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open table %s",
2104
25
                 pszTableName);
2105
25
        CPLFree(pszFname);
2106
25
        return nullptr;
2107
25
    }
2108
    /* To please Coverity */
2109
4.66k
    if (sTableDef.numFields < 0 || sTableDef.numFields >= 32767)
2110
55
    {
2111
55
        CPLError(CE_Failure, CPLE_OpenFailed, "Invalid numFields in %s",
2112
55
                 pszTableName);
2113
55
        CPLFree(pszFname);
2114
55
        return nullptr;
2115
55
    }
2116
2117
    /*-----------------------------------------------------------------
2118
     * Establish the location of the data file... depends on the
2119
     * szExternal[] field.
2120
     *----------------------------------------------------------------*/
2121
4.60k
    if (EQUAL(sTableDef.szExternal, "XX"))
2122
1.40k
    {
2123
        /*-------------------------------------------------------------
2124
         * The data file is located outside of the INFO directory.
2125
         * Read the path to the data file from the arc####.dat file
2126
         *------------------------------------------------------------*/
2127
1.40k
        _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "dat",
2128
1.40k
                                   eCoverType, pszFname, nFnameLen);
2129
1.40k
        AVCAdjustCaseSensitiveFilename(pszFname);
2130
2131
1.40k
        hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2132
1.40k
                              psDBCSInfo);
2133
2134
1.40k
        if (hFile)
2135
1.38k
        {
2136
            /* Read the relative file path, and remove trailing spaces.
2137
             */
2138
1.38k
            AVCRawBinReadBytes(hFile, 80, (GByte *)sTableDef.szDataFile);
2139
1.38k
            sTableDef.szDataFile[80] = '\0';
2140
2141
1.38k
            for (i = (int)strlen(sTableDef.szDataFile) - 1;
2142
3.92k
                 i >= 0 && isspace((unsigned char)sTableDef.szDataFile[i]); i--)
2143
2.53k
            {
2144
2.53k
                sTableDef.szDataFile[i] = '\0';
2145
2.53k
            }
2146
2147
1.38k
            AVCRawBinClose(hFile);
2148
1.38k
        }
2149
18
        else
2150
18
        {
2151
18
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s",
2152
18
                     pszFname);
2153
18
            CPLFree(pszFname);
2154
18
            return nullptr;
2155
18
        }
2156
1.40k
    }
2157
3.19k
    else
2158
3.19k
    {
2159
        /*-------------------------------------------------------------
2160
         * The data file IS the arc####.dat file
2161
         * Note: sTableDef.szDataFile must be relative to info directory
2162
         *------------------------------------------------------------*/
2163
3.19k
        _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "dat",
2164
3.19k
                                   eCoverType, pszFname, nFnameLen);
2165
3.19k
        snprintf(sTableDef.szDataFile, sizeof(sTableDef.szDataFile), "%s",
2166
3.19k
                 pszFname + strlen(pszInfoPath));
2167
3.19k
    }
2168
2169
    /*-----------------------------------------------------------------
2170
     * Read the table field definitions from the "arc####.nit" file.
2171
     *----------------------------------------------------------------*/
2172
4.58k
    _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "nit",
2173
4.58k
                               eCoverType, pszFname, nFnameLen);
2174
4.58k
    AVCAdjustCaseSensitiveFilename(pszFname);
2175
2176
4.58k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2177
4.58k
                          psDBCSInfo);
2178
2179
4.58k
    if (hFile)
2180
4.56k
    {
2181
4.56k
        int iField;
2182
2183
4.56k
        pasFieldDef = (AVCFieldInfo *)CPLCalloc(sTableDef.numFields,
2184
4.56k
                                                sizeof(AVCFieldInfo));
2185
2186
        /*-------------------------------------------------------------
2187
         * There must be at least sTableDef.numFields valid entries
2188
         * in the .NIT file...
2189
         *
2190
         * Note that we ignore any deleted field entries (entries with
2191
         * index=-1)... I don't see any use for these deleted fields...
2192
         * and I don't understand why Arc/Info includes them in their
2193
         * E00 table headers...
2194
         *------------------------------------------------------------*/
2195
24.6k
        for (i = 0, iField = 0; iField < sTableDef.numFields; i++)
2196
20.1k
        {
2197
20.1k
            if (_AVCBinReadNextArcNit(hFile, &(pasFieldDef[iField])) != 0)
2198
118
            {
2199
                /* Problems.... is the NIT file corrupt???
2200
                 */
2201
118
                AVCRawBinClose(hFile);
2202
118
                CPLFree(pszFname);
2203
118
                CPLFree(pasFieldDef);
2204
118
                CPLError(CE_Failure, CPLE_FileIO,
2205
118
                         "Failed reading table field info for table %s "
2206
118
                         "File may be corrupt?",
2207
118
                         pszTableName);
2208
118
                return nullptr;
2209
118
            }
2210
2211
            /*---------------------------------------------------------
2212
             * Check if the field has been deleted (nIndex == -1).
2213
             * We just ignore deleted fields
2214
             *--------------------------------------------------------*/
2215
20.0k
            if (pasFieldDef[iField].nIndex > 0)
2216
13.7k
                iField++;
2217
20.0k
        }
2218
2219
4.44k
        AVCRawBinClose(hFile);
2220
4.44k
    }
2221
21
    else
2222
21
    {
2223
21
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s",
2224
21
                 pszFname);
2225
21
        CPLFree(pszFname);
2226
21
        return nullptr;
2227
21
    }
2228
2229
    /*-----------------------------------------------------------------
2230
     * Open the data file... ready to read records from it.
2231
     * If the header says that table has 0 records, then we don't
2232
     * try to open the file... but we don't consider that as an error.
2233
     *----------------------------------------------------------------*/
2234
4.44k
    if (sTableDef.numRecords > 0 &&
2235
4.36k
        AVCFileExists(pszInfoPath, sTableDef.szDataFile))
2236
3.11k
    {
2237
3.11k
        VSIStatBufL sStatBuf;
2238
2239
3.11k
        snprintf(pszFname, nFnameLen, "%s%s", pszInfoPath,
2240
3.11k
                 sTableDef.szDataFile);
2241
3.11k
        AVCAdjustCaseSensitiveFilename(pszFname);
2242
2243
3.11k
        hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2244
3.11k
                              psDBCSInfo);
2245
2246
        /* OOPS... data file does not exist!
2247
         */
2248
3.11k
        if (hFile == nullptr)
2249
0
        {
2250
0
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s",
2251
0
                     pszFname);
2252
0
            CPLFree(pszFname);
2253
0
            return nullptr;
2254
0
        }
2255
2256
        /*-------------------------------------------------------------
2257
         * In some cases, the number of records field for a table in the
2258
         * arc.dir does not correspond to the real number of records
2259
         * in the data file.  In this kind of situation, the number of
2260
         * records returned by Arc/Info in an E00 file will be based
2261
         * on the real data file size, and not on the value from the arc.dir.
2262
         *
2263
         * Fetch the data file size, and correct the number of record
2264
         * field in the table header if necessary.
2265
         *------------------------------------------------------------*/
2266
3.11k
        if (VSIStatL(pszFname, &sStatBuf) != -1 && sTableDef.nRecSize > 0 &&
2267
704
            sStatBuf.st_size / sTableDef.nRecSize != sTableDef.numRecords)
2268
642
        {
2269
642
            sTableDef.numRecords = (int)(sStatBuf.st_size / sTableDef.nRecSize);
2270
642
        }
2271
3.11k
    }
2272
1.33k
    else
2273
1.33k
    {
2274
1.33k
        hFile = nullptr;
2275
1.33k
        sTableDef.numRecords = 0;
2276
1.33k
    }
2277
2278
    /*-----------------------------------------------------------------
2279
     * Alloc. and init. the AVCBinFile structure.
2280
     *----------------------------------------------------------------*/
2281
4.44k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
2282
2283
4.44k
    psFile->psRawBinFile = hFile;
2284
4.44k
    psFile->eCoverType = AVCCoverV7;
2285
4.44k
    psFile->eFileType = AVCFileTABLE;
2286
4.44k
    psFile->pszFilename = pszFname;
2287
2288
4.44k
    psFile->hdr.psTableDef = (AVCTableDef *)CPLMalloc(sizeof(AVCTableDef));
2289
4.44k
    *(psFile->hdr.psTableDef) = sTableDef;
2290
2291
4.44k
    psFile->hdr.psTableDef->pasFieldDef = pasFieldDef;
2292
2293
    /* We can't really tell the precision from a Table header...
2294
     * just set an arbitrary value... it probably won't be used anyways!
2295
     */
2296
4.44k
    psFile->nPrecision = AVC_SINGLE_PREC;
2297
2298
    /*-----------------------------------------------------------------
2299
     * Allocate temp. structures to use to read records from the file
2300
     * And allocate buffers for those fields that are stored as strings.
2301
     *----------------------------------------------------------------*/
2302
4.44k
    psFile->cur.pasFields =
2303
4.44k
        (AVCField *)CPLCalloc(sTableDef.numFields, sizeof(AVCField));
2304
2305
17.7k
    for (i = 0; i < sTableDef.numFields; i++)
2306
13.3k
    {
2307
13.3k
        if (pasFieldDef[i].nType1 * 10 == AVC_FT_DATE ||
2308
13.1k
            pasFieldDef[i].nType1 * 10 == AVC_FT_CHAR ||
2309
10.9k
            pasFieldDef[i].nType1 * 10 == AVC_FT_FIXINT ||
2310
10.0k
            pasFieldDef[i].nType1 * 10 == AVC_FT_FIXNUM)
2311
9.56k
        {
2312
9.56k
            psFile->cur.pasFields[i].pszStr =
2313
9.56k
                (GByte *)CPLCalloc(pasFieldDef[i].nSize + 1, sizeof(char));
2314
9.56k
        }
2315
13.3k
    }
2316
2317
4.44k
    return psFile;
2318
4.44k
}
2319
2320
/**********************************************************************
2321
 *                         _AVCBinReadNextTableRec()
2322
 *
2323
 * (This function is for internal library use... external calls should
2324
 * go to AVCBinReadNextTableRec() instead)
2325
 *
2326
 * Reads the next record from an attribute table and fills the
2327
 * pasFields[] array.
2328
 *
2329
 * Note that it is assumed that the pasFields[] array has been properly
2330
 * initialized, re the allocation of buffers for fields stored as
2331
 * strings.
2332
 *
2333
 * Returns 0 on success or -1 on error.
2334
 **********************************************************************/
2335
static int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields,
2336
                                   AVCFieldInfo *pasDef, AVCField *pasFields,
2337
                                   int nRecordSize)
2338
95.0k
{
2339
95.0k
    int i, nType, nBytesRead = 0;
2340
2341
95.0k
    if (psFile == nullptr)
2342
0
        return -1;
2343
2344
439k
    for (i = 0; i < nFields; i++)
2345
346k
    {
2346
346k
        if (AVCRawBinEOF(psFile))
2347
516
            return -1;
2348
2349
346k
        nType = pasDef[i].nType1 * 10;
2350
2351
346k
        if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR ||
2352
253k
            nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
2353
325k
        {
2354
            /*---------------------------------------------------------
2355
             * Values stored as strings
2356
             *--------------------------------------------------------*/
2357
325k
            AVCRawBinReadString(psFile, pasDef[i].nSize, pasFields[i].pszStr);
2358
325k
            pasFields[i].pszStr[pasDef[i].nSize] = '\0';
2359
325k
        }
2360
20.7k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
2361
8.46k
        {
2362
            /*---------------------------------------------------------
2363
             * 32 bit binary integers
2364
             *--------------------------------------------------------*/
2365
8.46k
            pasFields[i].nInt32 = AVCRawBinReadInt32(psFile);
2366
8.46k
        }
2367
12.2k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
2368
510
        {
2369
            /*---------------------------------------------------------
2370
             * 16 bit binary integers
2371
             *--------------------------------------------------------*/
2372
510
            pasFields[i].nInt16 = AVCRawBinReadInt16(psFile);
2373
510
        }
2374
11.7k
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
2375
8.57k
        {
2376
            /*---------------------------------------------------------
2377
             * Single precision floats
2378
             *--------------------------------------------------------*/
2379
8.57k
            pasFields[i].fFloat = AVCRawBinReadFloat(psFile);
2380
8.57k
        }
2381
3.16k
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
2382
899
        {
2383
            /*---------------------------------------------------------
2384
             * Double precision floats
2385
             *--------------------------------------------------------*/
2386
899
            pasFields[i].dDouble = AVCRawBinReadDouble(psFile);
2387
899
        }
2388
2.27k
        else
2389
2.27k
        {
2390
            /*---------------------------------------------------------
2391
             * Hummm... unsupported field type...
2392
             *--------------------------------------------------------*/
2393
2.27k
            CPLError(CE_Failure, CPLE_NotSupported,
2394
2.27k
                     "Unsupported field type: (type=%d, size=%d)", nType,
2395
2.27k
                     pasDef[i].nSize);
2396
2.27k
            return -1;
2397
2.27k
        }
2398
2399
344k
        nBytesRead += pasDef[i].nSize;
2400
344k
    }
2401
2402
    /*-----------------------------------------------------------------
2403
     * Record size is rounded to a multiple of 2 bytes.
2404
     * Check the number of bytes read, and move the read pointer if
2405
     * necessary.
2406
     *----------------------------------------------------------------*/
2407
92.2k
    if (nBytesRead < nRecordSize)
2408
321
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
2409
2410
92.2k
    return 0;
2411
95.0k
}
2412
2413
/*=====================================================================
2414
 *                         PC Arc/Info DBF TABLEs
2415
 *====================================================================*/
2416
2417
void _AVCBinReadRepairDBFFieldName(char *pszFieldName);
2418
2419
/**********************************************************************
2420
 *                         _AVCBinReadOpenDBFTable()
2421
 *
2422
 * (This function is for internal library use... external calls should
2423
 * go to AVCBinReadOpen() with type AVCCoverPC/AVCFileTABLE instead)
2424
 *
2425
 * Open the DBF table, reads the header information and inits the
2426
 * AVCBinFile handle to be ready to read records from it.
2427
 *
2428
 * Returns a valid AVCBinFile handle, or nullptr if the file could
2429
 * not be opened.
2430
 *
2431
 * _AVCBinReadCloseDBFTable() will eventually have to be called to release the
2432
 * resources used by the AVCBinFile structure.
2433
 **********************************************************************/
2434
AVCBinFile *_AVCBinReadOpenDBFTable(const char *pszDBFFilename,
2435
                                    const char *pszArcInfoTableName)
2436
289
{
2437
289
    AVCBinFile *psFile;
2438
289
    DBFHandle hDBFFile = nullptr;
2439
289
    int iField;
2440
289
    AVCTableDef *psTableDef;
2441
289
    AVCFieldInfo *pasFieldDef;
2442
2443
    /*-----------------------------------------------------------------
2444
     * Try to open the DBF file
2445
     *----------------------------------------------------------------*/
2446
289
    if ((hDBFFile = DBFOpen(pszDBFFilename, "rb")) == nullptr)
2447
86
    {
2448
86
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open table %s",
2449
86
                 pszDBFFilename);
2450
86
        return nullptr;
2451
86
    }
2452
2453
    /*-----------------------------------------------------------------
2454
     * Alloc. and init. the AVCBinFile structure.
2455
     *----------------------------------------------------------------*/
2456
203
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
2457
2458
203
    psFile->hDBFFile = hDBFFile;
2459
2460
203
    psFile->eCoverType = AVCCoverPC;
2461
203
    psFile->eFileType = AVCFileTABLE;
2462
203
    psFile->pszFilename = CPLStrdup(pszDBFFilename);
2463
2464
203
    psFile->hdr.psTableDef = nullptr;
2465
2466
    /* nCurDBFRecord is used to keep track of the 0-based index of the
2467
     * last record we read from the DBF file... this is to emulate
2468
     * sequential access which is assumed by the rest of the lib.
2469
     * Since the first record (record 0) has not been read yet, then
2470
     * we init the index at -1.
2471
     */
2472
203
    psFile->nCurDBFRecord = -1;
2473
2474
    /* We can't really tell the precision from a Table header...
2475
     * just set an arbitrary value... it probably won't be used anyways!
2476
     */
2477
203
    psFile->nPrecision = AVC_SINGLE_PREC;
2478
2479
    /*-----------------------------------------------------------------
2480
     * Build TableDef from the info in the DBF header
2481
     *----------------------------------------------------------------*/
2482
    /* Use calloc() to init some unused struct members */
2483
203
    psTableDef = (AVCTableDef *)CPLCalloc(1, sizeof(AVCTableDef));
2484
203
    psFile->hdr.psTableDef = psTableDef;
2485
2486
203
    snprintf(psTableDef->szTableName, sizeof(psTableDef->szTableName),
2487
203
             "%-32.32s", pszArcInfoTableName);
2488
2489
203
    psTableDef->numFields = (GInt16)DBFGetFieldCount(hDBFFile);
2490
2491
    /* We'll compute nRecSize value when we read fields info later */
2492
203
    psTableDef->nRecSize = 0;
2493
2494
203
    psTableDef->numRecords = DBFGetRecordCount(hDBFFile);
2495
2496
    /* All DBF tables are considered External */
2497
203
    strcpy(psTableDef->szExternal, "XX");
2498
2499
    /*-----------------------------------------------------------------
2500
     * Build Field definitions
2501
     *----------------------------------------------------------------*/
2502
203
    pasFieldDef =
2503
203
        (AVCFieldInfo *)CPLCalloc(psTableDef->numFields, sizeof(AVCFieldInfo));
2504
2505
203
    psTableDef->pasFieldDef = pasFieldDef;
2506
2507
9.23k
    for (iField = 0; iField < psTableDef->numFields; iField++)
2508
9.03k
    {
2509
9.03k
        int nWidth = 0, nDecimals = 0;
2510
        /* DBFFieldType eDBFType; */
2511
2512
        /*-------------------------------------------------------------
2513
         * Fetch DBF Field info and convert to Arc/Info type...
2514
         * Note that since DBF fields names are limited to 10 chars,
2515
         * we do not have to worry about field name length in the process.
2516
         *------------------------------------------------------------*/
2517
        /* eDBFType = */
2518
9.03k
        DBFGetFieldInfo(hDBFFile, iField, pasFieldDef[iField].szName, &nWidth,
2519
9.03k
                        &nDecimals);
2520
9.03k
        const char cNativeType = DBFGetNativeFieldType(hDBFFile, iField);
2521
2522
9.03k
        pasFieldDef[iField].nFmtWidth = (GInt16)nWidth;
2523
9.03k
        pasFieldDef[iField].nFmtPrec = (GInt16)nDecimals;
2524
2525
        /* nIndex is the 1-based field index that we see in the E00 header */
2526
9.03k
        pasFieldDef[iField].nIndex = (GInt16)(iField + 1);
2527
2528
9.03k
        if (cNativeType == 'F' || (cNativeType == 'N' && nDecimals > 0))
2529
799
        {
2530
            /*---------------------------------------------------------
2531
             * BINARY FLOAT
2532
             *--------------------------------------------------------*/
2533
799
            pasFieldDef[iField].nType1 = AVC_FT_BINFLOAT / 10;
2534
799
            pasFieldDef[iField].nSize = 4;
2535
799
            pasFieldDef[iField].nFmtWidth = 12; /* PC Arc/Info ignores the */
2536
799
            pasFieldDef[iField].nFmtPrec = 3;   /* DBF width/precision     */
2537
799
        }
2538
8.23k
        else if (cNativeType == 'N')
2539
172
        {
2540
            /*---------------------------------------------------------
2541
             * BINARY INTEGER
2542
             *--------------------------------------------------------*/
2543
172
            pasFieldDef[iField].nType1 = AVC_FT_BININT / 10;
2544
172
            pasFieldDef[iField].nSize = 4;
2545
172
            pasFieldDef[iField].nFmtWidth = 5; /* PC Arc/Info ignores the */
2546
172
            pasFieldDef[iField].nFmtPrec = -1; /* DBF width/precision     */
2547
2548
            /*---------------------------------------------------------
2549
             * Some special integer fields need to have their names
2550
             * repaired because DBF does not support special characters.
2551
             *--------------------------------------------------------*/
2552
172
            _AVCBinReadRepairDBFFieldName(pasFieldDef[iField].szName);
2553
172
        }
2554
8.06k
        else if (cNativeType == 'D')
2555
410
        {
2556
            /*---------------------------------------------------------
2557
             * DATE - Actually handled as a string internally
2558
             *--------------------------------------------------------*/
2559
410
            pasFieldDef[iField].nType1 = AVC_FT_DATE / 10;
2560
410
            pasFieldDef[iField].nSize = (GInt16)nWidth;
2561
410
            pasFieldDef[iField].nFmtPrec = -1;
2562
410
        }
2563
7.65k
        else /* (cNativeType == 'C' || cNativeType == 'L') */
2564
7.65k
        {
2565
            /*---------------------------------------------------------
2566
             * CHAR STRINGS ... and all unknown types also handled as strings
2567
             *--------------------------------------------------------*/
2568
7.65k
            pasFieldDef[iField].nType1 = AVC_FT_CHAR / 10;
2569
7.65k
            pasFieldDef[iField].nSize = (GInt16)nWidth;
2570
7.65k
            pasFieldDef[iField].nFmtPrec = -1;
2571
7.65k
        }
2572
2573
        /*---------------------------------------------------------
2574
         * Keep track of position of field in record... first one always
2575
         * starts at offset=1
2576
         *--------------------------------------------------------*/
2577
9.03k
        if (iField == 0)
2578
188
            pasFieldDef[iField].nOffset = 1;
2579
8.84k
        else
2580
8.84k
            pasFieldDef[iField].nOffset = (pasFieldDef[iField - 1].nOffset +
2581
8.84k
                                           pasFieldDef[iField - 1].nSize);
2582
2583
        /*---------------------------------------------------------
2584
         * Set default values for all other unused members in the struct
2585
         *--------------------------------------------------------*/
2586
9.03k
        pasFieldDef[iField].v2 = -1;    /* Always -1 ? */
2587
9.03k
        pasFieldDef[iField].v4 = 4;     /* Always 4 ?  */
2588
9.03k
        pasFieldDef[iField].v5 = -1;    /* Always -1 ? */
2589
9.03k
        pasFieldDef[iField].nType2 = 0; /* Always 0 ?  */
2590
9.03k
        pasFieldDef[iField].v10 = -1;   /* Always -1 ? */
2591
9.03k
        pasFieldDef[iField].v11 = -1;   /* Always -1 ? */
2592
9.03k
        pasFieldDef[iField].v12 = -1;   /* Always -1 ? */
2593
9.03k
        pasFieldDef[iField].v13 = -1;   /* Always -1 ? */
2594
9.03k
    }
2595
2596
    /*-----------------------------------------------------------------
2597
     * Compute record size...
2598
     * Record size has to be rounded to a multiple of 2 bytes.
2599
     *----------------------------------------------------------------*/
2600
203
    if (psTableDef->numFields > 0)
2601
188
    {
2602
188
        psTableDef->nRecSize =
2603
188
            (pasFieldDef[psTableDef->numFields - 1].nOffset - 1 +
2604
188
             pasFieldDef[psTableDef->numFields - 1].nSize);
2605
188
        psTableDef->nRecSize = ((psTableDef->nRecSize + 1) / 2) * 2;
2606
188
    }
2607
15
    else
2608
15
        psTableDef->nRecSize = 0;
2609
2610
    /*-----------------------------------------------------------------
2611
     * Allocate temp. structures to use to read records from the file
2612
     * And allocate buffers for those fields that are stored as strings.
2613
     *----------------------------------------------------------------*/
2614
203
    psFile->cur.pasFields =
2615
203
        (AVCField *)CPLCalloc(psTableDef->numFields, sizeof(AVCField));
2616
2617
9.23k
    for (iField = 0; iField < psTableDef->numFields; iField++)
2618
9.03k
    {
2619
9.03k
        if (pasFieldDef[iField].nType1 * 10 == AVC_FT_DATE ||
2620
8.62k
            pasFieldDef[iField].nType1 * 10 == AVC_FT_CHAR ||
2621
971
            pasFieldDef[iField].nType1 * 10 == AVC_FT_FIXINT ||
2622
971
            pasFieldDef[iField].nType1 * 10 == AVC_FT_FIXNUM)
2623
8.06k
        {
2624
8.06k
            psFile->cur.pasFields[iField].pszStr = (GByte *)CPLCalloc(
2625
8.06k
                pasFieldDef[iField].nSize + 1, sizeof(GByte));
2626
8.06k
        }
2627
9.03k
    }
2628
2629
203
    return psFile;
2630
289
}
2631
2632
/**********************************************************************
2633
 *                         _AVCBinReadNextDBFTableRec()
2634
 *
2635
 * (This function is for internal library use... external calls should
2636
 * go to AVCBinReadNextTableRec() instead)
2637
 *
2638
 * Reads the next record from a AVCCoverPC DBF attribute table and fills the
2639
 * pasFields[] array.
2640
 *
2641
 * Note that it is assumed that the pasFields[] array has been properly
2642
 * initialized, re the allocation of buffers for fields stored as
2643
 * strings.
2644
 *
2645
 * Returns 0 on success or -1 on error.
2646
 **********************************************************************/
2647
static int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex,
2648
                                      int nFields, AVCFieldInfo *pasDef,
2649
                                      AVCField *pasFields)
2650
0
{
2651
0
    int i, nType;
2652
2653
    /*-----------------------------------------------------------------
2654
     * Increment current record index.
2655
     * We use nCurDBFRecord to keep track of the 0-based index of the
2656
     * last record we read from the DBF file... this is to emulate
2657
     * sequential access which is assumed by the rest of the lib.
2658
     *----------------------------------------------------------------*/
2659
0
    if (hDBFFile == nullptr || piRecordIndex == nullptr || pasDef == nullptr ||
2660
0
        pasFields == nullptr)
2661
0
        return -1;
2662
2663
0
    (*piRecordIndex)++;
2664
2665
0
    if (*piRecordIndex >= DBFGetRecordCount(hDBFFile))
2666
0
        return -1; /* Reached EOF */
2667
2668
    /*-----------------------------------------------------------------
2669
     * Read/convert each field based on type
2670
     *----------------------------------------------------------------*/
2671
0
    for (i = 0; i < nFields; i++)
2672
0
    {
2673
0
        nType = pasDef[i].nType1 * 10;
2674
2675
0
        if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR ||
2676
0
            nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
2677
0
        {
2678
            /*---------------------------------------------------------
2679
             * Values stored as strings
2680
             *--------------------------------------------------------*/
2681
0
            const char *pszValue;
2682
0
            pszValue = DBFReadStringAttribute(hDBFFile, *piRecordIndex, i);
2683
0
            strncpy((char *)pasFields[i].pszStr, pszValue, pasDef[i].nSize);
2684
0
            pasFields[i].pszStr[pasDef[i].nSize] = '\0';
2685
0
        }
2686
0
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
2687
0
        {
2688
            /*---------------------------------------------------------
2689
             * 32 bit binary integers
2690
             *--------------------------------------------------------*/
2691
0
            pasFields[i].nInt32 =
2692
0
                DBFReadIntegerAttribute(hDBFFile, *piRecordIndex, i);
2693
0
        }
2694
0
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
2695
0
        {
2696
            /*---------------------------------------------------------
2697
             * 16 bit binary integers
2698
             *--------------------------------------------------------*/
2699
0
            pasFields[i].nInt16 =
2700
0
                (GInt16)DBFReadIntegerAttribute(hDBFFile, *piRecordIndex, i);
2701
0
        }
2702
0
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
2703
0
        {
2704
            /*---------------------------------------------------------
2705
             * Single precision floats
2706
             *--------------------------------------------------------*/
2707
0
            pasFields[i].fFloat =
2708
0
                (float)DBFReadDoubleAttribute(hDBFFile, *piRecordIndex, i);
2709
0
        }
2710
0
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
2711
0
        {
2712
            /*---------------------------------------------------------
2713
             * Double precision floats
2714
             *--------------------------------------------------------*/
2715
0
            pasFields[i].dDouble =
2716
0
                DBFReadDoubleAttribute(hDBFFile, *piRecordIndex, i);
2717
0
        }
2718
0
        else
2719
0
        {
2720
            /*---------------------------------------------------------
2721
             * Hummm... unsupported field type...
2722
             *--------------------------------------------------------*/
2723
0
            CPLError(CE_Failure, CPLE_NotSupported,
2724
0
                     "Unsupported field type: (type=%d, size=%d)", nType,
2725
0
                     pasDef[i].nSize);
2726
0
            return -1;
2727
0
        }
2728
0
    }
2729
2730
0
    return 0;
2731
0
}
2732
2733
/**********************************************************************
2734
 *                         _AVCBinReadRepairDBFFieldName()
2735
 *
2736
 * Attempt to repair some special integer field names that usually
2737
 * carry special chars such as '#' or '-' but that are lost because of
2738
 * DBF limitations and are replaced by '_'.
2739
 *
2740
 **********************************************************************/
2741
void _AVCBinReadRepairDBFFieldName(char *pszFieldName)
2742
172
{
2743
172
    char *pszTmp;
2744
2745
172
    if ((pszTmp = strrchr(pszFieldName, '_')) == nullptr)
2746
114
        return; /* No special char to process */
2747
2748
    /*-----------------------------------------------------------------
2749
     * Replace '_' at end of field name by a '#', as in:
2750
     *   COVER# , FNODE#, TNODE#, LPOLY#, RPOLY#
2751
     *
2752
     * and replace names that end with "_ID" with "-ID" as in COVER-ID
2753
     *----------------------------------------------------------------*/
2754
58
    if (EQUAL(pszTmp, "_"))
2755
20
        *pszTmp = '#';
2756
38
    else if (EQUAL(pszTmp, "_ID"))
2757
7
        *pszTmp = '-';
2758
58
}