Coverage Report

Created: 2025-11-15 08:43

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
37.0k
{
238
37.0k
    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
37.0k
    if (eFileType == AVCFileTABLE)
245
4.76k
    {
246
4.76k
        if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2)
247
267
            return _AVCBinReadOpenDBFTable(pszPath, pszName);
248
4.50k
        else
249
4.50k
            return _AVCBinReadOpenTable(pszPath, pszName, eCoverType,
250
4.50k
                                        psDBCSInfo);
251
4.76k
    }
252
253
    /*-----------------------------------------------------------------
254
     * PRJ files are text files... we won't use the AVCRawBin*()
255
     * functions for them...
256
     *----------------------------------------------------------------*/
257
32.3k
    if (eFileType == AVCFilePRJ)
258
11.6k
    {
259
11.6k
        return _AVCBinReadOpenPrj(pszPath, pszName);
260
11.6k
    }
261
262
    /*-----------------------------------------------------------------
263
     * All other file types share a very similar opening method.
264
     *----------------------------------------------------------------*/
265
20.6k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
266
267
20.6k
    psFile->eFileType = eFileType;
268
20.6k
    psFile->eCoverType = eCoverType;
269
270
20.6k
    psFile->pszFilename =
271
20.6k
        (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
272
20.6k
    snprintf(psFile->pszFilename, strlen(pszPath) + strlen(pszName) + 1, "%s%s",
273
20.6k
             pszPath, pszName);
274
275
20.6k
    AVCAdjustCaseSensitiveFilename(psFile->pszFilename);
276
277
20.6k
    psFile->psRawBinFile = AVCRawBinOpen(
278
20.6k
        psFile->pszFilename, "r", AVC_COVER_BYTE_ORDER(eCoverType), psDBCSInfo);
279
280
20.6k
    if (psFile->psRawBinFile == nullptr)
281
130
    {
282
        /* Failed to open file... just return nullptr since an error message
283
         * has already been issued by AVCRawBinOpen()
284
         */
285
130
        CPLFree(psFile->pszFilename);
286
130
        CPLFree(psFile);
287
130
        return nullptr;
288
130
    }
289
290
    /*-----------------------------------------------------------------
291
     * Read the header, and set the precision field if applicable
292
     *----------------------------------------------------------------*/
293
20.5k
    if (AVCBinReadRewind(psFile) != 0)
294
2.04k
    {
295
2.04k
        AVCRawBinClose(psFile->psRawBinFile);
296
2.04k
        CPLFree(psFile->pszFilename);
297
2.04k
        CPLFree(psFile);
298
2.04k
        return nullptr;
299
2.04k
    }
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
18.5k
    if (psFile->eFileType == AVCFileARC)
307
1.49k
    {
308
1.49k
        psFile->cur.psArc = (AVCArc *)CPLCalloc(1, sizeof(AVCArc));
309
1.49k
    }
310
17.0k
    else if (psFile->eFileType == AVCFilePAL || psFile->eFileType == AVCFileRPL)
311
8.35k
    {
312
8.35k
        psFile->cur.psPal = (AVCPal *)CPLCalloc(1, sizeof(AVCPal));
313
8.35k
    }
314
8.66k
    else if (psFile->eFileType == AVCFileCNT)
315
469
    {
316
469
        psFile->cur.psCnt = (AVCCnt *)CPLCalloc(1, sizeof(AVCCnt));
317
469
    }
318
8.19k
    else if (psFile->eFileType == AVCFileLAB)
319
2.97k
    {
320
2.97k
        psFile->cur.psLab = (AVCLab *)CPLCalloc(1, sizeof(AVCLab));
321
2.97k
    }
322
5.22k
    else if (psFile->eFileType == AVCFileTOL)
323
29
    {
324
29
        psFile->cur.psTol = (AVCTol *)CPLCalloc(1, sizeof(AVCTol));
325
29
    }
326
5.19k
    else if (psFile->eFileType == AVCFileTXT || psFile->eFileType == AVCFileTX6)
327
5.08k
    {
328
5.08k
        psFile->cur.psTxt = (AVCTxt *)CPLCalloc(1, sizeof(AVCTxt));
329
5.08k
    }
330
113
    else if (psFile->eFileType == AVCFileRXP)
331
113
    {
332
113
        psFile->cur.psRxp = (AVCRxp *)CPLCalloc(1, sizeof(AVCRxp));
333
113
    }
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
18.5k
    return psFile;
346
20.5k
}
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
34.5k
{
356
34.5k
    AVCRawBinClose(psFile->psRawBinFile);
357
34.5k
    psFile->psRawBinFile = nullptr;
358
359
34.5k
    CPLFree(psFile->pszFilename);
360
34.5k
    psFile->pszFilename = nullptr;
361
362
34.5k
    if (psFile->hDBFFile)
363
189
        DBFClose(psFile->hDBFFile);
364
365
34.5k
    if (psFile->psIndexFile != nullptr)
366
457
        AVCRawBinClose(psFile->psIndexFile);
367
368
34.5k
    if (psFile->eFileType == AVCFileARC)
369
1.49k
    {
370
1.49k
        if (psFile->cur.psArc)
371
1.49k
            CPLFree(psFile->cur.psArc->pasVertices);
372
1.49k
        CPLFree(psFile->cur.psArc);
373
1.49k
    }
374
33.0k
    else if (psFile->eFileType == AVCFilePAL || psFile->eFileType == AVCFileRPL)
375
8.35k
    {
376
8.35k
        if (psFile->cur.psPal)
377
8.35k
            CPLFree(psFile->cur.psPal->pasArcs);
378
8.35k
        CPLFree(psFile->cur.psPal);
379
8.35k
    }
380
24.6k
    else if (psFile->eFileType == AVCFileCNT)
381
469
    {
382
469
        if (psFile->cur.psCnt)
383
469
            CPLFree(psFile->cur.psCnt->panLabelIds);
384
469
        CPLFree(psFile->cur.psCnt);
385
469
    }
386
24.1k
    else if (psFile->eFileType == AVCFileLAB)
387
2.97k
    {
388
2.97k
        CPLFree(psFile->cur.psLab);
389
2.97k
    }
390
21.2k
    else if (psFile->eFileType == AVCFileTOL)
391
29
    {
392
29
        CPLFree(psFile->cur.psTol);
393
29
    }
394
21.1k
    else if (psFile->eFileType == AVCFilePRJ)
395
11.5k
    {
396
11.5k
        CSLDestroy(psFile->cur.papszPrj);
397
11.5k
    }
398
9.63k
    else if (psFile->eFileType == AVCFileTXT || psFile->eFileType == AVCFileTX6)
399
5.08k
    {
400
5.08k
        if (psFile->cur.psTxt)
401
5.08k
        {
402
5.08k
            CPLFree(psFile->cur.psTxt->pasVertices);
403
5.08k
            CPLFree(psFile->cur.psTxt->pszText);
404
5.08k
        }
405
5.08k
        CPLFree(psFile->cur.psTxt);
406
5.08k
    }
407
4.55k
    else if (psFile->eFileType == AVCFileRXP)
408
113
    {
409
113
        CPLFree(psFile->cur.psRxp);
410
113
    }
411
4.43k
    else if (psFile->eFileType == AVCFileTABLE)
412
4.43k
    {
413
4.43k
        _AVCDestroyTableFields(psFile->hdr.psTableDef, psFile->cur.pasFields);
414
4.43k
        _AVCDestroyTableDef(psFile->hdr.psTableDef);
415
4.43k
    }
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
34.5k
    CPLFree(psFile);
423
34.5k
}
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
20.4k
{
439
20.4k
    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
20.4k
    if (eCoverType == AVCCoverPC)
449
365
        AVCRawBinFSeek(psFile, 256, SEEK_SET);
450
20.0k
    else
451
20.0k
        AVCRawBinFSeek(psFile, 0, SEEK_SET);
452
453
20.4k
    psHeader->nSignature = AVCRawBinReadInt32(psFile);
454
455
20.4k
    if (AVCRawBinEOF(psFile))
456
25
        nStatus = -1;
457
458
20.4k
    psHeader->nPrecision = AVCRawBinReadInt32(psFile);
459
20.4k
    psHeader->nRecordSize = AVCRawBinReadInt32(psFile);
460
461
    /* Jump to 24th byte in header */
462
20.4k
    AVCRawBinFSeek(psFile, 12, SEEK_CUR);
463
20.4k
    psHeader->nLength = AVCRawBinReadInt32(psFile);
464
20.4k
    if (psHeader->nLength < 0 || psHeader->nLength > (INT_MAX - 256) / 2)
465
1.37k
    {
466
1.37k
        return -1;
467
1.37k
    }
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
19.0k
    if (eCoverType == AVCCoverPC)
475
353
        AVCRawBinSetFileDataSize(psFile, psHeader->nLength * 2 + 256);
476
18.6k
    else
477
18.6k
        AVCRawBinSetFileDataSize(psFile, psHeader->nLength * 2);
478
479
    /* Move the pointer at the end of the 100 bytes header
480
     */
481
19.0k
    AVCRawBinFSeek(psFile, 72, SEEK_CUR);
482
483
19.0k
    return nStatus;
484
20.4k
}
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
20.5k
{
498
20.5k
    AVCBinHeader sHeader;
499
20.5k
    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
20.5k
    AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
513
514
20.5k
    if (psFile->eFileType == AVCFileARC || psFile->eFileType == AVCFilePAL ||
515
16.2k
        psFile->eFileType == AVCFileRPL || psFile->eFileType == AVCFileCNT ||
516
8.55k
        psFile->eFileType == AVCFileLAB || psFile->eFileType == AVCFileTXT ||
517
4.87k
        psFile->eFileType == AVCFileTX6)
518
20.4k
    {
519
20.4k
        nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
520
20.4k
                                    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
20.4k
        if ((sHeader.nPrecision < 0 || sHeader.nPrecision > 1000) &&
534
9.72k
            psFile->eCoverType != AVCCoverPC)
535
9.47k
            psFile->nPrecision = AVC_DOUBLE_PREC;
536
10.9k
        else
537
10.9k
            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
20.4k
        if (sHeader.nSignature != 9993 && sHeader.nSignature != 9994)
543
1.11k
        {
544
1.11k
            CPLError(CE_Warning, CPLE_AssertionFailed,
545
1.11k
                     "%s appears to have an invalid file header.",
546
1.11k
                     psFile->pszFilename);
547
1.11k
            return -2;
548
1.11k
        }
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
19.2k
        if (psFile->eCoverType == AVCCoverWeird &&
558
253
            psFile->eFileType == AVCFileTXT &&
559
55
            (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
19.2k
    }
565
144
    else if (psFile->eFileType == AVCFileTOL)
566
31
    {
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
31
        int nSignature = 0;
581
31
        nSignature = AVCRawBinReadInt32(psFile->psRawBinFile);
582
583
31
        if (nSignature == 9993)
584
2
        {
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
2
            nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader,
590
2
                                        psFile->eCoverType);
591
592
2
            psFile->nPrecision = AVC_DOUBLE_PREC;
593
2
        }
594
29
        else
595
29
        {
596
            /* It's a single precision tol.adf ... just set the
597
             * precision field.
598
             */
599
29
            AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET);
600
29
            psFile->nPrecision = AVC_SINGLE_PREC;
601
29
        }
602
31
    }
603
604
19.4k
    return nStatus;
605
20.5k
}
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
144k
{
620
144k
    int bIndexed = FALSE;
621
144k
    int nObjectOffset, nRecordSize = 0, nRecordStart = 0, nLen;
622
    /* cppcheck-suppress unreadVariable */
623
144k
    char szExt[4] = {0, 0, 0, 0};
624
144k
    char *pszExt = szExt;
625
626
144k
    if (iObjIndex < 0)
627
3.98k
        return nullptr;
628
629
    /*-----------------------------------------------------------------
630
     * Determine some information from based on the coverage type.
631
     *----------------------------------------------------------------*/
632
140k
    nLen = (int)strlen(psFile->pszFilename);
633
140k
    if (psFile->eFileType == AVCFileARC &&
634
26.6k
        ((nLen >= 3 &&
635
26.6k
          STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 3), "arc")) ||
636
26.6k
         (nLen >= 7 && STARTS_WITH_CI((pszExt = psFile->pszFilename + nLen - 7),
637
26.6k
                                      "arc.adf"))))
638
26.6k
    {
639
26.6k
        bIndexed = TRUE;
640
26.6k
    }
641
114k
    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
114k
    else if (psFile->eFileType == AVCFileTABLE)
652
114k
    {
653
114k
        bIndexed = FALSE;
654
114k
        nRecordSize = psFile->hdr.psTableDef->nRecSize;
655
114k
        nRecordStart = 0;
656
114k
    }
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
140k
    if (bIndexed && psFile->psIndexFile == nullptr)
665
2.42k
    {
666
2.42k
        char chOrig;
667
668
2.42k
        chOrig = pszExt[2];
669
2.42k
        if (chOrig > 'A' && chOrig < 'Z')
670
285
            pszExt[2] = 'X';
671
2.14k
        else
672
2.14k
            pszExt[2] = 'x';
673
674
2.42k
        psFile->psIndexFile = AVCRawBinOpen(psFile->pszFilename, "rb",
675
2.42k
                                            psFile->psRawBinFile->eByteOrder,
676
2.42k
                                            psFile->psRawBinFile->psDBCSInfo);
677
2.42k
        pszExt[2] = chOrig;
678
679
2.42k
        if (psFile->psIndexFile == nullptr)
680
1.97k
            return nullptr;
681
2.42k
    }
682
683
    /*-----------------------------------------------------------------
684
     * Establish the offset to read the object from.
685
     *----------------------------------------------------------------*/
686
138k
    if (bIndexed)
687
24.6k
    {
688
24.6k
        GIntBig nIndexOffsetBig;
689
690
24.6k
        if (psFile->eCoverType == AVCCoverPC)
691
0
            nIndexOffsetBig = 356 + static_cast<GIntBig>(iObjIndex - 1) * 8;
692
24.6k
        else
693
24.6k
            nIndexOffsetBig = 100 + static_cast<GIntBig>(iObjIndex - 1) * 8;
694
24.6k
        if (nIndexOffsetBig < INT_MIN || nIndexOffsetBig > INT_MAX)
695
608
            return nullptr;
696
697
24.0k
        const int nIndexOffset = static_cast<int>(nIndexOffsetBig);
698
24.0k
        AVCRawBinFSeek(psFile->psIndexFile, nIndexOffset, SEEK_SET);
699
24.0k
        if (AVCRawBinEOF(psFile->psIndexFile))
700
2.08k
            return nullptr;
701
702
21.9k
        nObjectOffset = AVCRawBinReadInt32(psFile->psIndexFile);
703
21.9k
        if (nObjectOffset < INT_MIN / 2 || nObjectOffset > (INT_MAX - 256) / 2)
704
641
            return nullptr;
705
21.3k
        nObjectOffset *= 2;
706
707
21.3k
        if (psFile->eCoverType == AVCCoverPC)
708
0
            nObjectOffset += 256;
709
21.3k
    }
710
114k
    else
711
114k
    {
712
114k
        GIntBig nObjectOffsetBig =
713
114k
            nRecordStart + nRecordSize * static_cast<GIntBig>(iObjIndex - 1);
714
114k
        if (nObjectOffsetBig < INT_MIN || nObjectOffsetBig > INT_MAX)
715
12.4k
            return nullptr;
716
101k
        nObjectOffset = static_cast<int>(nObjectOffsetBig);
717
101k
    }
718
719
    /*-----------------------------------------------------------------
720
     * Seek to the start of the object in the data file.
721
     *----------------------------------------------------------------*/
722
122k
    AVCRawBinFSeek(psFile->psRawBinFile, nObjectOffset, SEEK_SET);
723
122k
    if (AVCRawBinEOF(psFile->psRawBinFile))
724
24.7k
        return nullptr;
725
726
    /*-----------------------------------------------------------------
727
     * Read and return the object.
728
     *----------------------------------------------------------------*/
729
98.1k
    return AVCBinReadNextObject(psFile);
730
122k
}
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
266k
{
748
266k
    void *psObj = nullptr;
749
750
266k
    switch (psFile->eFileType)
751
266k
    {
752
22.5k
        case AVCFileARC:
753
22.5k
            psObj = (void *)AVCBinReadNextArc(psFile);
754
22.5k
            break;
755
3.38k
        case AVCFilePAL:
756
19.5k
        case AVCFileRPL:
757
19.5k
            psObj = (void *)AVCBinReadNextPal(psFile);
758
19.5k
            break;
759
4.03k
        case AVCFileCNT:
760
4.03k
            psObj = (void *)AVCBinReadNextCnt(psFile);
761
4.03k
            break;
762
135k
        case AVCFileLAB:
763
135k
            psObj = (void *)AVCBinReadNextLab(psFile);
764
135k
            break;
765
0
        case AVCFileTOL:
766
0
            psObj = (void *)AVCBinReadNextTol(psFile);
767
0
            break;
768
2.49k
        case AVCFileTXT:
769
7.54k
        case AVCFileTX6:
770
7.54k
            psObj = (void *)AVCBinReadNextTxt(psFile);
771
7.54k
            break;
772
0
        case AVCFileRXP:
773
0
            psObj = (void *)AVCBinReadNextRxp(psFile);
774
0
            break;
775
77.2k
        case AVCFileTABLE:
776
77.2k
            psObj = (void *)AVCBinReadNextTableRec(psFile);
777
77.2k
            break;
778
0
        default:
779
0
            CPLError(CE_Failure, CPLE_IllegalArg,
780
0
                     "AVCBinReadNextObject(): Unsupported file type!");
781
266k
    }
782
783
266k
    return psObj;
784
266k
}
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
77.2k
{
797
77.2k
    if (psFile->eCoverType != AVCCoverPC && psFile->eCoverType != AVCCoverPC2 &&
798
77.2k
        psFile->eFileType == AVCFileTABLE &&
799
77.2k
        psFile->hdr.psTableDef->numRecords > 0 &&
800
76.8k
        !AVCRawBinEOF(psFile->psRawBinFile) &&
801
76.8k
        _AVCBinReadNextTableRec(
802
76.8k
            psFile->psRawBinFile, psFile->hdr.psTableDef->numFields,
803
76.8k
            psFile->hdr.psTableDef->pasFieldDef, psFile->cur.pasFields,
804
76.8k
            psFile->hdr.psTableDef->nRecSize) == 0)
805
73.9k
    {
806
73.9k
        return psFile->cur.pasFields;
807
73.9k
    }
808
3.38k
    else if ((psFile->eCoverType == AVCCoverPC ||
809
3.38k
              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.38k
    return nullptr;
822
77.2k
}
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
22.4k
{
845
22.4k
    int i, numVertices;
846
22.4k
    int nRecordSize, nStartPos, nBytesRead;
847
848
22.4k
    psArc->nArcId = AVCRawBinReadInt32(psFile);
849
22.4k
    if (AVCRawBinEOF(psFile))
850
211
        return -1;
851
852
22.2k
    nRecordSize = AVCRawBinReadInt32(psFile);
853
22.2k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
854
880
        return -1;
855
21.3k
    nRecordSize *= 2;
856
21.3k
    nStartPos = psFile->nCurPos + psFile->nOffset;
857
21.3k
    psArc->nUserId = AVCRawBinReadInt32(psFile);
858
21.3k
    psArc->nFNode = AVCRawBinReadInt32(psFile);
859
21.3k
    psArc->nTNode = AVCRawBinReadInt32(psFile);
860
21.3k
    psArc->nLPoly = AVCRawBinReadInt32(psFile);
861
21.3k
    psArc->nRPoly = AVCRawBinReadInt32(psFile);
862
21.3k
    numVertices = AVCRawBinReadInt32(psFile);
863
21.3k
    if (numVertices < 0 || numVertices > 100 * 1024 * 1024)
864
695
        return -1;
865
20.6k
    if (numVertices > 10 * 1024 * 1024 &&
866
308
        !AVCRawBinIsFileGreaterThan(
867
308
            psFile,
868
308
            cpl::fits_on<int>(numVertices *
869
308
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
870
308
    {
871
308
        return -1;
872
308
    }
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
20.3k
    if (psArc->pasVertices == nullptr || numVertices > psArc->numVertices)
880
1.82k
    {
881
1.82k
        AVCVertex *pasNewVertices = (AVCVertex *)VSIRealloc(
882
1.82k
            psArc->pasVertices, numVertices * sizeof(AVCVertex));
883
1.82k
        if (pasNewVertices == nullptr)
884
0
            return -1;
885
1.82k
        psArc->pasVertices = pasNewVertices;
886
1.82k
    }
887
888
20.3k
    psArc->numVertices = numVertices;
889
890
20.3k
    if (nPrecision == AVC_SINGLE_PREC)
891
8.23k
    {
892
264k
        for (i = 0; i < numVertices; i++)
893
256k
        {
894
256k
            psArc->pasVertices[i].x = AVCRawBinReadFloat(psFile);
895
256k
            psArc->pasVertices[i].y = AVCRawBinReadFloat(psFile);
896
256k
            if (psFile->nCurSize == 0)
897
236
                return -1;
898
256k
        }
899
8.23k
    }
900
12.1k
    else
901
12.1k
    {
902
235k
        for (i = 0; i < numVertices; i++)
903
223k
        {
904
223k
            psArc->pasVertices[i].x = AVCRawBinReadDouble(psFile);
905
223k
            psArc->pasVertices[i].y = AVCRawBinReadDouble(psFile);
906
223k
            if (psFile->nCurSize == 0)
907
265
                return -1;
908
223k
        }
909
12.1k
    }
910
911
    /*-----------------------------------------------------------------
912
     * Record size may be larger than number of vertices.  Skip up to
913
     * start of next object.
914
     *----------------------------------------------------------------*/
915
19.8k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
916
19.8k
    if (nBytesRead < nRecordSize)
917
4.38k
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
918
919
19.8k
    return 0;
920
20.3k
}
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
22.5k
{
933
22.5k
    if (psFile->eFileType != AVCFileARC || AVCRawBinEOF(psFile->psRawBinFile) ||
934
22.4k
        _AVCBinReadNextArc(psFile->psRawBinFile, psFile->cur.psArc,
935
22.4k
                           psFile->nPrecision) != 0)
936
2.68k
    {
937
2.68k
        return nullptr;
938
2.68k
    }
939
940
19.8k
    return psFile->cur.psArc;
941
22.5k
}
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
18.2k
{
964
18.2k
    int i, numArcs;
965
18.2k
    int nRecordSize, nStartPos, nBytesRead;
966
967
18.2k
    psPal->nPolyId = AVCRawBinReadInt32(psFile);
968
18.2k
    nRecordSize = AVCRawBinReadInt32(psFile);
969
18.2k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
970
986
        return -1;
971
17.2k
    nRecordSize *= 2;
972
17.2k
    nStartPos = psFile->nCurPos + psFile->nOffset;
973
974
17.2k
    if (AVCRawBinEOF(psFile))
975
55
        return -1;
976
977
17.2k
    if (nPrecision == AVC_SINGLE_PREC)
978
2.04k
    {
979
2.04k
        psPal->sMin.x = AVCRawBinReadFloat(psFile);
980
2.04k
        psPal->sMin.y = AVCRawBinReadFloat(psFile);
981
2.04k
        psPal->sMax.x = AVCRawBinReadFloat(psFile);
982
2.04k
        psPal->sMax.y = AVCRawBinReadFloat(psFile);
983
2.04k
    }
984
15.1k
    else
985
15.1k
    {
986
15.1k
        psPal->sMin.x = AVCRawBinReadDouble(psFile);
987
15.1k
        psPal->sMin.y = AVCRawBinReadDouble(psFile);
988
15.1k
        psPal->sMax.x = AVCRawBinReadDouble(psFile);
989
15.1k
        psPal->sMax.y = AVCRawBinReadDouble(psFile);
990
15.1k
    }
991
992
17.2k
    numArcs = AVCRawBinReadInt32(psFile);
993
17.2k
    if (numArcs < 0 || numArcs > 100 * 1024 * 1024)
994
460
        return -1;
995
16.7k
    if (numArcs > 10 * 1024 * 1024 &&
996
47
        !AVCRawBinIsFileGreaterThan(psFile, numArcs * sizeof(int) * 3))
997
47
    {
998
47
        return -1;
999
47
    }
1000
1001
    /* Realloc the arc list array only if it needs to grow...
1002
     * do not realloc to a smaller size.
1003
     */
1004
16.7k
    if (psPal->pasArcs == nullptr || numArcs > psPal->numArcs)
1005
5.94k
    {
1006
5.94k
        AVCPalArc *pasNewArcs = (AVCPalArc *)VSIRealloc(
1007
5.94k
            psPal->pasArcs, numArcs * sizeof(AVCPalArc));
1008
5.94k
        if (pasNewArcs == nullptr)
1009
0
            return -1;
1010
5.94k
        psPal->pasArcs = pasNewArcs;
1011
5.94k
    }
1012
1013
16.7k
    psPal->numArcs = numArcs;
1014
1015
127k
    for (i = 0; i < numArcs; i++)
1016
110k
    {
1017
110k
        psPal->pasArcs[i].nArcId = AVCRawBinReadInt32(psFile);
1018
110k
        psPal->pasArcs[i].nFNode = AVCRawBinReadInt32(psFile);
1019
110k
        psPal->pasArcs[i].nAdjPoly = AVCRawBinReadInt32(psFile);
1020
110k
        if (psFile->nCurSize == 0)
1021
181
            return -1;
1022
110k
    }
1023
1024
    /*-----------------------------------------------------------------
1025
     * Record size may be larger than number of vertices.  Skip up to
1026
     * start of next object.
1027
     *----------------------------------------------------------------*/
1028
16.5k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
1029
16.5k
    if (nBytesRead < nRecordSize)
1030
623
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
1031
1032
16.5k
    return 0;
1033
16.7k
}
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
19.5k
{
1046
19.5k
    if ((psFile->eFileType != AVCFilePAL && psFile->eFileType != AVCFileRPL) ||
1047
19.5k
        AVCRawBinEOF(psFile->psRawBinFile) ||
1048
18.2k
        _AVCBinReadNextPal(psFile->psRawBinFile, psFile->cur.psPal,
1049
18.2k
                           psFile->nPrecision) != 0)
1050
3.00k
    {
1051
3.00k
        return nullptr;
1052
3.00k
    }
1053
1054
16.5k
    return psFile->cur.psPal;
1055
19.5k
}
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.97k
{
1074
3.97k
    int i, numLabels;
1075
3.97k
    int nRecordSize, nStartPos, nBytesRead;
1076
1077
3.97k
    psCnt->nPolyId = AVCRawBinReadInt32(psFile);
1078
3.97k
    nRecordSize = AVCRawBinReadInt32(psFile);
1079
3.97k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1080
92
        return -1;
1081
3.87k
    nRecordSize *= 2;
1082
3.87k
    nStartPos = psFile->nCurPos + psFile->nOffset;
1083
1084
3.87k
    if (AVCRawBinEOF(psFile))
1085
6
        return -1;
1086
1087
3.87k
    if (nPrecision == AVC_SINGLE_PREC)
1088
1.68k
    {
1089
1.68k
        psCnt->sCoord.x = AVCRawBinReadFloat(psFile);
1090
1.68k
        psCnt->sCoord.y = AVCRawBinReadFloat(psFile);
1091
1.68k
    }
1092
2.18k
    else
1093
2.18k
    {
1094
2.18k
        psCnt->sCoord.x = AVCRawBinReadDouble(psFile);
1095
2.18k
        psCnt->sCoord.y = AVCRawBinReadDouble(psFile);
1096
2.18k
    }
1097
1098
3.87k
    numLabels = AVCRawBinReadInt32(psFile);
1099
3.87k
    if (numLabels < 0 || numLabels > 100 * 1024 * 1024)
1100
27
        return -1;
1101
3.84k
    if (numLabels > 10 * 1024 * 1024 &&
1102
13
        !AVCRawBinIsFileGreaterThan(psFile, numLabels * sizeof(int)))
1103
13
    {
1104
13
        return -1;
1105
13
    }
1106
1107
    /* Realloc the LabelIds array only if it needs to grow...
1108
     * do not realloc to a smaller size.
1109
     */
1110
3.83k
    if (psCnt->panLabelIds == nullptr || numLabels > psCnt->numLabels)
1111
263
    {
1112
263
        GInt32 *panIds = (GInt32 *)VSIRealloc(psCnt->panLabelIds,
1113
263
                                              numLabels * sizeof(GInt32));
1114
263
        if (panIds == nullptr)
1115
0
            return -1;
1116
263
        psCnt->panLabelIds = panIds;
1117
263
    }
1118
1119
3.83k
    psCnt->numLabels = numLabels;
1120
1121
116k
    for (i = 0; i < numLabels; i++)
1122
112k
    {
1123
112k
        psCnt->panLabelIds[i] = AVCRawBinReadInt32(psFile);
1124
112k
        if (psFile->nCurSize == 0)
1125
31
            return -1;
1126
112k
    }
1127
1128
    /*-----------------------------------------------------------------
1129
     * Record size may be larger than number of vertices.  Skip up to
1130
     * start of next object.
1131
     *----------------------------------------------------------------*/
1132
3.80k
    nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos;
1133
3.80k
    if (nBytesRead < nRecordSize)
1134
326
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
1135
1136
3.80k
    return 0;
1137
3.83k
}
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
4.03k
{
1150
4.03k
    if (psFile->eFileType != AVCFileCNT || AVCRawBinEOF(psFile->psRawBinFile) ||
1151
3.97k
        _AVCBinReadNextCnt(psFile->psRawBinFile, psFile->cur.psCnt,
1152
3.97k
                           psFile->nPrecision) != 0)
1153
234
    {
1154
234
        return nullptr;
1155
234
    }
1156
1157
3.80k
    return psFile->cur.psCnt;
1158
4.03k
}
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
134k
{
1177
1178
134k
    psLab->nValue = AVCRawBinReadInt32(psFile);
1179
134k
    psLab->nPolyId = AVCRawBinReadInt32(psFile);
1180
1181
134k
    if (AVCRawBinEOF(psFile))
1182
347
        return -1;
1183
1184
133k
    if (nPrecision == AVC_SINGLE_PREC)
1185
129k
    {
1186
129k
        psLab->sCoord1.x = AVCRawBinReadFloat(psFile);
1187
129k
        psLab->sCoord1.y = AVCRawBinReadFloat(psFile);
1188
129k
        psLab->sCoord2.x = AVCRawBinReadFloat(psFile);
1189
129k
        psLab->sCoord2.y = AVCRawBinReadFloat(psFile);
1190
129k
        psLab->sCoord3.x = AVCRawBinReadFloat(psFile);
1191
129k
        psLab->sCoord3.y = AVCRawBinReadFloat(psFile);
1192
129k
    }
1193
4.32k
    else
1194
4.32k
    {
1195
4.32k
        psLab->sCoord1.x = AVCRawBinReadDouble(psFile);
1196
4.32k
        psLab->sCoord1.y = AVCRawBinReadDouble(psFile);
1197
4.32k
        psLab->sCoord2.x = AVCRawBinReadDouble(psFile);
1198
4.32k
        psLab->sCoord2.y = AVCRawBinReadDouble(psFile);
1199
4.32k
        psLab->sCoord3.x = AVCRawBinReadDouble(psFile);
1200
4.32k
        psLab->sCoord3.y = AVCRawBinReadDouble(psFile);
1201
4.32k
    }
1202
1203
133k
    return 0;
1204
134k
}
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
135k
{
1217
135k
    if (psFile->eFileType != AVCFileLAB || AVCRawBinEOF(psFile->psRawBinFile) ||
1218
134k
        _AVCBinReadNextLab(psFile->psRawBinFile, psFile->cur.psLab,
1219
134k
                           psFile->nPrecision) != 0)
1220
1.48k
    {
1221
1.48k
        return nullptr;
1222
1.48k
    }
1223
1224
133k
    return psFile->cur.psLab;
1225
135k
}
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
11.6k
{
1301
11.6k
    AVCBinFile *psFile;
1302
11.6k
    char *pszFname, **papszPrj;
1303
1304
    /*-----------------------------------------------------------------
1305
     * Load the PRJ file contents into a stringlist.
1306
     *----------------------------------------------------------------*/
1307
11.6k
    pszFname = (char *)CPLMalloc(strlen(pszPath) + strlen(pszName) + 1);
1308
11.6k
    snprintf(pszFname, strlen(pszPath) + strlen(pszName) + 1, "%s%s", pszPath,
1309
11.6k
             pszName);
1310
1311
11.6k
    papszPrj = CSLLoad2(pszFname, 50, 160, nullptr);
1312
1313
11.6k
    CPLFree(pszFname);
1314
1315
11.6k
    if (papszPrj == nullptr)
1316
64
    {
1317
        /* Failed to open file... just return nullptr since an error message
1318
         * has already been issued by CSLLoad()
1319
         */
1320
64
        return nullptr;
1321
64
    }
1322
1323
    /*-----------------------------------------------------------------
1324
     * Alloc and init the AVCBinFile handle.
1325
     *----------------------------------------------------------------*/
1326
11.5k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
1327
1328
11.5k
    psFile->eFileType = AVCFilePRJ;
1329
11.5k
    psFile->psRawBinFile = nullptr;
1330
11.5k
    psFile->cur.papszPrj = papszPrj;
1331
11.5k
    psFile->pszFilename = nullptr;
1332
1333
11.5k
    return psFile;
1334
11.6k
}
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.5k
{
1351
    /*-----------------------------------------------------------------
1352
     * The file should have already been loaded by AVCBinFileOpen(),
1353
     * so there is not much to do here!
1354
     *----------------------------------------------------------------*/
1355
11.5k
    return psFile->cur.papszPrj;
1356
11.5k
}
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.46k
{
1375
6.46k
    int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1376
6.46k
    int numBytesRead;
1377
1378
6.46k
    numVerticesBefore =
1379
6.46k
        ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1380
1381
6.46k
    psTxt->nTxtId = AVCRawBinReadInt32(psFile);
1382
6.46k
    if (AVCRawBinEOF(psFile))
1383
39
        return -1;
1384
1385
6.42k
    nRecordSize = AVCRawBinReadInt32(psFile);
1386
6.42k
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1387
355
        return -1;
1388
6.07k
    nRecordSize = nRecordSize * 2 + 8;
1389
1390
6.07k
    psTxt->nUserId = AVCRawBinReadInt32(psFile);
1391
6.07k
    psTxt->nLevel = AVCRawBinReadInt32(psFile);
1392
1393
6.07k
    psTxt->f_1e2 = AVCRawBinReadFloat(psFile);
1394
6.07k
    psTxt->nSymbol = AVCRawBinReadInt32(psFile);
1395
6.07k
    psTxt->numVerticesLine = AVCRawBinReadInt32(psFile);
1396
6.07k
    psTxt->n28 = AVCRawBinReadInt32(psFile);
1397
6.07k
    psTxt->numChars = AVCRawBinReadInt32(psFile);
1398
6.07k
    if (psTxt->numChars < 0 || psTxt->numChars > 10 * 1024 * 1024)
1399
110
        return -1;
1400
5.96k
    psTxt->numVerticesArrow = AVCRawBinReadInt32(psFile);
1401
1402
125k
    for (i = 0; i < 20; i++)
1403
119k
    {
1404
119k
        psTxt->anJust1[i] = AVCRawBinReadInt16(psFile);
1405
119k
    }
1406
125k
    for (i = 0; i < 20; i++)
1407
119k
    {
1408
119k
        psTxt->anJust2[i] = AVCRawBinReadInt16(psFile);
1409
119k
    }
1410
1411
5.96k
    if (nPrecision == AVC_SINGLE_PREC)
1412
5.32k
    {
1413
5.32k
        psTxt->dHeight = AVCRawBinReadFloat(psFile);
1414
5.32k
        psTxt->dV2 = AVCRawBinReadFloat(psFile);
1415
5.32k
        psTxt->dV3 = AVCRawBinReadFloat(psFile);
1416
5.32k
    }
1417
642
    else
1418
642
    {
1419
642
        psTxt->dHeight = AVCRawBinReadDouble(psFile);
1420
642
        psTxt->dV2 = AVCRawBinReadDouble(psFile);
1421
642
        psTxt->dV3 = AVCRawBinReadDouble(psFile);
1422
642
    }
1423
1424
5.96k
    numCharsToRead = ((int)(psTxt->numChars + 3) / 4) * 4;
1425
5.96k
    if (psTxt->pszText == nullptr ||
1426
5.37k
        ((int)(strlen((char *)psTxt->pszText) + 3) / 4) * 4 < numCharsToRead)
1427
892
    {
1428
892
        GByte *pszNewText = (GByte *)VSIRealloc(
1429
892
            psTxt->pszText, (numCharsToRead + 1) * sizeof(char));
1430
892
        if (pszNewText == nullptr)
1431
0
            return -1;
1432
892
        psTxt->pszText = pszNewText;
1433
892
    }
1434
1435
5.96k
    AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1436
5.96k
    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.96k
    if (psTxt->numVerticesLine == INT_MIN ||
1442
5.96k
        psTxt->numVerticesArrow == INT_MIN ||
1443
5.93k
        ABS(psTxt->numVerticesLine) >
1444
5.93k
            100 * 1024 * 1024 - ABS(psTxt->numVerticesArrow))
1445
91
        return -1;
1446
5.87k
    numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1447
5.87k
    if (numVertices > 10 * 1024 * 1024 &&
1448
47
        !AVCRawBinIsFileGreaterThan(
1449
47
            psFile,
1450
47
            cpl::fits_on<int>(numVertices *
1451
47
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
1452
47
    {
1453
47
        return -1;
1454
47
    }
1455
1456
5.82k
    if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1457
2.66k
        psTxt->pasVertices = (AVCVertex *)CPLRealloc(
1458
2.66k
            psTxt->pasVertices, numVertices * sizeof(AVCVertex));
1459
1460
5.82k
    if (nPrecision == AVC_SINGLE_PREC)
1461
5.23k
    {
1462
89.0k
        for (i = 0; i < numVertices; i++)
1463
83.9k
        {
1464
83.9k
            psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1465
83.9k
            psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1466
83.9k
            if (psFile->nCurSize == 0)
1467
133
                return -1;
1468
83.9k
        }
1469
5.23k
    }
1470
591
    else
1471
591
    {
1472
4.77k
        for (i = 0; i < numVertices; i++)
1473
4.21k
        {
1474
4.21k
            psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1475
4.21k
            psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1476
4.21k
            if (psFile->nCurSize == 0)
1477
35
                return -1;
1478
4.21k
        }
1479
591
    }
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.65k
    if (nPrecision == AVC_SINGLE_PREC)
1487
5.10k
        numBytesRead = 132 + numCharsToRead + numVertices * 2 * 4;
1488
556
    else
1489
556
        numBytesRead = 144 + numCharsToRead + numVertices * 2 * 8;
1490
1491
5.65k
    if (numBytesRead < nRecordSize)
1492
181
        AVCRawBinFSeek(psFile, nRecordSize - numBytesRead, SEEK_CUR);
1493
1494
5.65k
    return 0;
1495
5.82k
}
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
342
{
1512
342
    int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize;
1513
1514
342
    numVerticesBefore =
1515
342
        ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow);
1516
1517
342
    psTxt->nTxtId = AVCRawBinReadInt32(psFile);
1518
342
    if (AVCRawBinEOF(psFile))
1519
2
        return -1;
1520
1521
340
    nRecordSize = AVCRawBinReadInt32(psFile);
1522
340
    if (nRecordSize < 0 || nRecordSize > 100 * 1024 * 1024)
1523
36
        return -1;
1524
304
    nRecordSize = nRecordSize * 2 + 8;
1525
1526
304
    psTxt->nUserId = 0;
1527
304
    psTxt->nLevel = AVCRawBinReadInt32(psFile);
1528
1529
304
    psTxt->numVerticesLine = AVCRawBinReadInt32(psFile);
1530
    /* We are not expecting more than 4 vertices */
1531
304
    psTxt->numVerticesLine = MIN(psTxt->numVerticesLine, 4);
1532
1533
304
    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
304
    psTxt->numVerticesLine += 1;
1543
304
    if (psTxt->numVerticesLine == INT_MIN ||
1544
304
        ABS(psTxt->numVerticesLine) > 100 * 1024 * 1024)
1545
6
        return -1;
1546
298
    numVertices = ABS(psTxt->numVerticesLine);
1547
298
    if (numVertices < 2)
1548
4
        return -1;
1549
294
    if (numVertices > 10 * 1024 * 1024 &&
1550
12
        !AVCRawBinIsFileGreaterThan(
1551
12
            psFile,
1552
12
            cpl::fits_on<int>(numVertices *
1553
12
                              ((nPrecision == AVC_SINGLE_PREC) ? 8 : 16))))
1554
12
    {
1555
12
        return -1;
1556
12
    }
1557
1558
282
    if (psTxt->pasVertices == nullptr || numVertices > numVerticesBefore)
1559
167
        psTxt->pasVertices = (AVCVertex *)CPLRealloc(
1560
167
            psTxt->pasVertices, numVertices * sizeof(AVCVertex));
1561
1562
63.5k
    for (i = 1; i < numVertices; i++)
1563
63.3k
    {
1564
63.3k
        if (nPrecision == AVC_SINGLE_PREC)
1565
63.1k
        {
1566
63.1k
            psTxt->pasVertices[i].x = AVCRawBinReadFloat(psFile);
1567
63.1k
            psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile);
1568
63.1k
            if (psFile->nCurSize == 0)
1569
19
                return -1;
1570
63.1k
        }
1571
191
        else
1572
191
        {
1573
191
            psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile);
1574
191
            psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile);
1575
191
            if (psFile->nCurSize == 0)
1576
7
                return -1;
1577
191
        }
1578
63.3k
    }
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
256
    psTxt->pasVertices[0].x = psTxt->pasVertices[1].x;
1583
256
    psTxt->pasVertices[0].y = psTxt->pasVertices[1].y;
1584
1585
    /* Skip the other floats (vertices) that are unused */
1586
256
    if (nPrecision == AVC_SINGLE_PREC)
1587
225
        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
256
    if (nPrecision == AVC_SINGLE_PREC)
1592
225
    {
1593
225
        psTxt->dHeight = AVCRawBinReadFloat(psFile);
1594
225
    }
1595
31
    else
1596
31
    {
1597
31
        psTxt->dHeight = AVCRawBinReadDouble(psFile);
1598
31
    }
1599
256
    psTxt->f_1e2 = AVCRawBinReadFloat(psFile);
1600
256
    psTxt->nSymbol = AVCRawBinReadInt32(psFile);
1601
256
    psTxt->numChars = AVCRawBinReadInt32(psFile);
1602
256
    if (psTxt->numChars < 0)
1603
14
        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
242
    if (nPrecision == AVC_SINGLE_PREC)
1610
213
    {
1611
213
        numCharsToRead = nRecordSize - (28 + 16 * 4);
1612
213
    }
1613
29
    else
1614
29
    {
1615
29
        numCharsToRead = nRecordSize - (28 + 16 * 8);
1616
29
    }
1617
242
    if (numCharsToRead < 0)
1618
7
        return -1;
1619
1620
    /* Do a quick check in case file is corrupt! */
1621
235
    psTxt->numChars = MIN(psTxt->numChars, numCharsToRead);
1622
1623
235
    if (psTxt->pszText == nullptr ||
1624
118
        ((int)(strlen((char *)psTxt->pszText) + 3) / 4) * 4 < numCharsToRead)
1625
210
    {
1626
210
        psTxt->pszText = (GByte *)CPLRealloc(
1627
210
            psTxt->pszText, (numCharsToRead + 5) * sizeof(char));
1628
210
    }
1629
1630
235
    AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText);
1631
235
    psTxt->pszText[psTxt->numChars] = '\0';
1632
1633
    /* Set unused members to default values...
1634
     */
1635
235
    psTxt->dV2 = 0.0;
1636
235
    psTxt->dV3 = 0.0;
1637
235
    psTxt->n28 = 0;
1638
4.93k
    for (i = 0; i < 20; i++)
1639
4.70k
    {
1640
4.70k
        psTxt->anJust1[i] = 0;
1641
4.70k
        psTxt->anJust2[i] = 0;
1642
4.70k
    }
1643
1644
235
    return 0;
1645
242
}
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.54k
{
1658
7.54k
    int nStatus = 0;
1659
1660
7.54k
    if ((psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6) ||
1661
7.54k
        AVCRawBinEOF(psFile->psRawBinFile))
1662
735
    {
1663
735
        return nullptr;
1664
735
    }
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.80k
    if (psFile->eFileType == AVCFileTXT &&
1673
2.38k
        (psFile->eCoverType == AVCCoverPC ||
1674
2.09k
         psFile->eCoverType == AVCCoverWeird))
1675
342
    {
1676
        /* TXT file in PC Coverages (and some Weird Coverages)
1677
         */
1678
342
        nStatus = _AVCBinReadNextPCCoverageTxt(
1679
342
            psFile->psRawBinFile, psFile->cur.psTxt, psFile->nPrecision);
1680
342
    }
1681
6.46k
    else
1682
6.46k
    {
1683
        /* TXT in V7 Coverages (and some Weird Coverages), and TX6/TX7 in
1684
         * all coverage types
1685
         */
1686
6.46k
        nStatus = _AVCBinReadNextTxt(psFile->psRawBinFile, psFile->cur.psTxt,
1687
6.46k
                                     psFile->nPrecision);
1688
6.46k
    }
1689
1690
6.80k
    if (nStatus != 0)
1691
917
    {
1692
917
        return nullptr;
1693
917
    }
1694
1695
5.89k
    return psFile->cur.psTxt;
1696
6.80k
}
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
97.1k
{
1767
97.1k
    int i;
1768
1769
    /* Arc/Info Table name
1770
     */
1771
97.1k
    AVCRawBinReadString(psFile, 32, (GByte *)psArcDir->szTableName);
1772
97.1k
    psArcDir->szTableName[32] = '\0';
1773
1774
97.1k
    if (AVCRawBinEOF(psFile))
1775
409
        return -1;
1776
1777
    /* "ARC####" basename for .DAT and .NIT files
1778
     */
1779
96.7k
    AVCRawBinReadString(psFile, 8, (GByte *)psArcDir->szInfoFile);
1780
96.7k
    psArcDir->szInfoFile[7] = '\0';
1781
119k
    for (i = 6; i > 0 && psArcDir->szInfoFile[i] == ' '; i--)
1782
23.0k
        psArcDir->szInfoFile[i] = '\0';
1783
1784
96.7k
    psArcDir->numFields = AVCRawBinReadInt16(psFile);
1785
96.7k
    psArcDir->nRecSize = AVCRawBinReadInt16(psFile);
1786
1787
96.7k
    AVCRawBinFSeek(psFile, 18, SEEK_CUR); /* Skip 18 bytes */
1788
1789
96.7k
    psArcDir->bDeletedFlag = AVCRawBinReadInt16(psFile);
1790
96.7k
    psArcDir->numRecords = AVCRawBinReadInt32(psFile);
1791
1792
96.7k
    AVCRawBinFSeek(psFile, 10, SEEK_CUR); /* Skip 10 bytes */
1793
1794
96.7k
    AVCRawBinReadBytes(psFile, 2, (GByte *)psArcDir->szExternal);
1795
96.7k
    psArcDir->szExternal[2] = '\0';
1796
1797
96.7k
    AVCRawBinFSeek(psFile, 300, SEEK_CUR); /* Skip the remaining 300 bytes */
1798
1799
96.7k
    return 0;
1800
97.1k
}
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
19.5k
{
1817
19.5k
    AVCRawBinReadString(psFile, 16, (GByte *)psField->szName);
1818
19.5k
    psField->szName[16] = '\0';
1819
1820
19.5k
    if (AVCRawBinEOF(psFile))
1821
55
        return -1;
1822
1823
19.4k
    psField->nSize = AVCRawBinReadInt16(psFile);
1824
19.4k
    if (psField->nSize < 0)
1825
60
        return -1;
1826
19.4k
    psField->v2 = AVCRawBinReadInt16(psFile); /* Always -1 ? */
1827
19.4k
    psField->nOffset = AVCRawBinReadInt16(psFile);
1828
19.4k
    psField->v4 = AVCRawBinReadInt16(psFile); /* Always 4 ?  */
1829
19.4k
    psField->v5 = AVCRawBinReadInt16(psFile); /* Always -1 ? */
1830
19.4k
    psField->nFmtWidth = AVCRawBinReadInt16(psFile);
1831
19.4k
    psField->nFmtPrec = AVCRawBinReadInt16(psFile);
1832
19.4k
    psField->nType1 = AVCRawBinReadInt16(psFile);
1833
19.4k
    psField->nType2 = AVCRawBinReadInt16(psFile); /* Always 0 ? */
1834
19.4k
    psField->v10 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1835
19.4k
    psField->v11 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1836
19.4k
    psField->v12 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1837
19.4k
    psField->v13 = AVCRawBinReadInt16(psFile);    /* Always -1 ? */
1838
1839
19.4k
    AVCRawBinReadString(psFile, 16,
1840
19.4k
                        (GByte *)psField->szAltName); /* Always Blank ? */
1841
19.4k
    psField->szAltName[16] = '\0';
1842
1843
19.4k
    AVCRawBinFSeek(psFile, 56, SEEK_CUR); /* Skip 56 bytes */
1844
1845
19.4k
    psField->nIndex = AVCRawBinReadInt16(psFile);
1846
1847
19.4k
    AVCRawBinFSeek(psFile, 28, SEEK_CUR); /* Skip the remaining 28 bytes */
1848
1849
19.4k
    return 0;
1850
19.4k
}
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
47.8k
{
1867
47.8k
    GBool bFilesExist = FALSE;
1868
47.8k
    char *pszBuf = nullptr;
1869
47.8k
    VSIStatBufL sStatBuf;
1870
47.8k
    size_t nBufLen;
1871
1872
47.8k
    if (pszRetFname)
1873
8.78k
    {
1874
8.78k
        pszBuf = pszRetFname;
1875
8.78k
        nBufLen = nRetFnameLen;
1876
8.78k
    }
1877
39.0k
    else
1878
39.0k
    {
1879
39.0k
        nBufLen = strlen(pszInfoPath) + strlen(pszBasename) + 10;
1880
39.0k
        pszBuf = (char *)CPLMalloc(nBufLen);
1881
39.0k
    }
1882
1883
47.8k
    if (eCoverType == AVCCoverWeird)
1884
249
    {
1885
249
        snprintf(pszBuf, nBufLen, "%s%s%s", pszInfoPath, pszBasename,
1886
249
                 pszDatOrNit);
1887
249
    }
1888
47.5k
    else
1889
47.5k
    {
1890
47.5k
        snprintf(pszBuf, nBufLen, "%s%s.%s", pszInfoPath, pszBasename,
1891
47.5k
                 pszDatOrNit);
1892
47.5k
    }
1893
1894
47.8k
    AVCAdjustCaseSensitiveFilename(pszBuf);
1895
1896
47.8k
    if (VSIStatL(pszBuf, &sStatBuf) == 0)
1897
30.9k
        bFilesExist = TRUE;
1898
1899
47.8k
    if (eCoverType == AVCCoverWeird && !bFilesExist)
1900
99
    {
1901
        /* In some cases, the filename can be truncated to 8 chars
1902
         * and we end up with "ARC000DA"... check that possibility.
1903
         */
1904
99
        pszBuf[strlen(pszBuf) - 1] = '\0';
1905
1906
99
        AVCAdjustCaseSensitiveFilename(pszBuf);
1907
1908
99
        if (VSIStatL(pszBuf, &sStatBuf) == 0)
1909
77
            bFilesExist = TRUE;
1910
99
    }
1911
1912
47.8k
    if (pszRetFname == nullptr)
1913
39.0k
        CPLFree(pszBuf);
1914
1915
47.8k
    return bFilesExist;
1916
47.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
27.4k
{
1931
1932
27.4k
    return (_AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, "dat",
1933
27.4k
                                       eCoverType, nullptr, 0) == TRUE &&
1934
11.5k
            _AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, "nit",
1935
11.5k
                                       eCoverType, nullptr, 0) == TRUE);
1936
27.4k
}
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
8.69k
{
1961
8.69k
    char **papszList = nullptr;
1962
8.69k
    char *pszFname;
1963
8.69k
    char szNameToFind[33] = "";
1964
8.69k
    int nLen;
1965
8.69k
    AVCRawBinFile *hFile;
1966
8.69k
    AVCTableDef sEntry;
1967
1968
8.69k
    if (ppapszArcDatFiles)
1969
8.69k
        *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
8.69k
    if (eCoverType == AVCCoverV7Tables)
1976
325
        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
8.69k
    if (pszCoverName != nullptr)
1984
        // cppcheck-suppress bufferAccessOutOfBounds
1985
8.36k
        snprintf(szNameToFind, sizeof(szNameToFind), "%-.28s.", pszCoverName);
1986
8.69k
    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
8.69k
    pszFname = (char *)CPLMalloc(strlen(pszInfoPath) + 9);
1994
8.69k
    if (eCoverType == AVCCoverWeird)
1995
52
        snprintf(pszFname, strlen(pszInfoPath) + 9, "%sarcdr9", pszInfoPath);
1996
8.64k
    else
1997
8.64k
        snprintf(pszFname, strlen(pszInfoPath) + 9, "%sarc.dir", pszInfoPath);
1998
1999
8.69k
    AVCAdjustCaseSensitiveFilename(pszFname);
2000
2001
8.69k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2002
8.69k
                          psDBCSInfo);
2003
2004
8.69k
    if (hFile)
2005
8.69k
    {
2006
88.3k
        while (!AVCRawBinEOF(hFile) &&
2007
80.0k
               _AVCBinReadNextArcDir(hFile, &sEntry) == 0)
2008
79.6k
        {
2009
79.6k
            if (/* sEntry.numRecords > 0 && (DO NOT skip empty tables) */
2010
79.6k
                !sEntry.bDeletedFlag &&
2011
37.4k
                (pszCoverName == nullptr ||
2012
16.1k
                 EQUALN(szNameToFind, sEntry.szTableName, nLen)) &&
2013
22.9k
                _AVCBinReadInfoFileExists(pszInfoPath, sEntry.szInfoFile,
2014
22.9k
                                          eCoverType))
2015
6.19k
            {
2016
6.19k
                papszList = CSLAddString(papszList, sEntry.szTableName);
2017
2018
6.19k
                if (ppapszArcDatFiles)
2019
6.19k
                    *ppapszArcDatFiles =
2020
6.19k
                        CSLAddString(*ppapszArcDatFiles, sEntry.szInfoFile);
2021
6.19k
            }
2022
79.6k
        }
2023
8.69k
        AVCRawBinClose(hFile);
2024
8.69k
    }
2025
2026
8.69k
    CPLFree(pszFname);
2027
2028
8.69k
    return papszList;
2029
8.69k
}
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.50k
{
2051
4.50k
    AVCBinFile *psFile;
2052
4.50k
    AVCRawBinFile *hFile;
2053
4.50k
    AVCTableDef sTableDef;
2054
4.50k
    AVCFieldInfo *pasFieldDef;
2055
4.50k
    char *pszFname;
2056
4.50k
    GBool bFound;
2057
4.50k
    int i;
2058
4.50k
    size_t nFnameLen;
2059
2060
4.50k
    memset(&sTableDef, 0, sizeof(sTableDef));
2061
4.50k
    sTableDef.numFields = 0;
2062
4.50k
    sTableDef.pasFieldDef = nullptr;
2063
2064
    /* Alloc a buffer big enough for the longest possible filename...
2065
     */
2066
4.50k
    nFnameLen = strlen(pszInfoPath) + 81;
2067
4.50k
    pszFname = (char *)CPLMalloc(nFnameLen);
2068
2069
    /*-----------------------------------------------------------------
2070
     * Fetch info about this table from the "arc.dir"
2071
     *----------------------------------------------------------------*/
2072
4.50k
    if (eCoverType == AVCCoverWeird)
2073
19
        snprintf(pszFname, nFnameLen, "%sarcdr9", pszInfoPath);
2074
4.48k
    else
2075
4.48k
        snprintf(pszFname, nFnameLen, "%sarc.dir", pszInfoPath);
2076
2077
4.50k
    AVCAdjustCaseSensitiveFilename(pszFname);
2078
2079
4.50k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2080
4.50k
                          psDBCSInfo);
2081
4.50k
    bFound = FALSE;
2082
2083
4.50k
    if (hFile)
2084
4.50k
    {
2085
21.5k
        while (!bFound && _AVCBinReadNextArcDir(hFile, &sTableDef) == 0)
2086
17.0k
        {
2087
17.0k
            if (!sTableDef.bDeletedFlag &&
2088
9.77k
                EQUALN(sTableDef.szTableName, pszTableName,
2089
17.0k
                       strlen(pszTableName)) &&
2090
4.54k
                _AVCBinReadInfoFileExists(pszInfoPath, sTableDef.szInfoFile,
2091
4.54k
                                          eCoverType))
2092
4.48k
            {
2093
4.48k
                bFound = TRUE;
2094
4.48k
            }
2095
17.0k
        }
2096
4.50k
        AVCRawBinClose(hFile);
2097
4.50k
    }
2098
2099
    /* Hummm... quite likely that this table does not exist!
2100
     */
2101
4.50k
    if (!bFound)
2102
17
    {
2103
17
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open table %s",
2104
17
                 pszTableName);
2105
17
        CPLFree(pszFname);
2106
17
        return nullptr;
2107
17
    }
2108
    /* To please Coverity */
2109
4.48k
    if (sTableDef.numFields < 0 || sTableDef.numFields >= 32767)
2110
81
    {
2111
81
        CPLError(CE_Failure, CPLE_OpenFailed, "Invalid numFields in %s",
2112
81
                 pszTableName);
2113
81
        CPLFree(pszFname);
2114
81
        return nullptr;
2115
81
    }
2116
2117
    /*-----------------------------------------------------------------
2118
     * Establish the location of the data file... depends on the
2119
     * szExternal[] field.
2120
     *----------------------------------------------------------------*/
2121
4.40k
    if (EQUAL(sTableDef.szExternal, "XX"))
2122
1.37k
    {
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.37k
        _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "dat",
2128
1.37k
                                   eCoverType, pszFname, nFnameLen);
2129
1.37k
        AVCAdjustCaseSensitiveFilename(pszFname);
2130
2131
1.37k
        hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2132
1.37k
                              psDBCSInfo);
2133
2134
1.37k
        if (hFile)
2135
1.35k
        {
2136
            /* Read the relative file path, and remove trailing spaces.
2137
             */
2138
1.35k
            AVCRawBinReadBytes(hFile, 80, (GByte *)sTableDef.szDataFile);
2139
1.35k
            sTableDef.szDataFile[80] = '\0';
2140
2141
1.35k
            for (i = (int)strlen(sTableDef.szDataFile) - 1;
2142
3.85k
                 i >= 0 && isspace((unsigned char)sTableDef.szDataFile[i]); i--)
2143
2.50k
            {
2144
2.50k
                sTableDef.szDataFile[i] = '\0';
2145
2.50k
            }
2146
2147
1.35k
            AVCRawBinClose(hFile);
2148
1.35k
        }
2149
19
        else
2150
19
        {
2151
19
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s",
2152
19
                     pszFname);
2153
19
            CPLFree(pszFname);
2154
19
            return nullptr;
2155
19
        }
2156
1.37k
    }
2157
3.03k
    else
2158
3.03k
    {
2159
        /*-------------------------------------------------------------
2160
         * The data file IS the arc####.dat file
2161
         * Note: sTableDef.szDataFile must be relative to info directory
2162
         *------------------------------------------------------------*/
2163
3.03k
        _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "dat",
2164
3.03k
                                   eCoverType, pszFname, nFnameLen);
2165
3.03k
        snprintf(sTableDef.szDataFile, sizeof(sTableDef.szDataFile), "%s",
2166
3.03k
                 pszFname + strlen(pszInfoPath));
2167
3.03k
    }
2168
2169
    /*-----------------------------------------------------------------
2170
     * Read the table field definitions from the "arc####.nit" file.
2171
     *----------------------------------------------------------------*/
2172
4.38k
    _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, "nit",
2173
4.38k
                               eCoverType, pszFname, nFnameLen);
2174
4.38k
    AVCAdjustCaseSensitiveFilename(pszFname);
2175
2176
4.38k
    hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2177
4.38k
                          psDBCSInfo);
2178
2179
4.38k
    if (hFile)
2180
4.36k
    {
2181
4.36k
        int iField;
2182
2183
4.36k
        pasFieldDef = (AVCFieldInfo *)CPLCalloc(sTableDef.numFields,
2184
4.36k
                                                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
23.7k
        for (i = 0, iField = 0; iField < sTableDef.numFields; i++)
2196
19.5k
        {
2197
19.5k
            if (_AVCBinReadNextArcNit(hFile, &(pasFieldDef[iField])) != 0)
2198
115
            {
2199
                /* Problems.... is the NIT file corrupt???
2200
                 */
2201
115
                AVCRawBinClose(hFile);
2202
115
                CPLFree(pszFname);
2203
115
                CPLFree(pasFieldDef);
2204
115
                CPLError(CE_Failure, CPLE_FileIO,
2205
115
                         "Failed reading table field info for table %s "
2206
115
                         "File may be corrupt?",
2207
115
                         pszTableName);
2208
115
                return nullptr;
2209
115
            }
2210
2211
            /*---------------------------------------------------------
2212
             * Check if the field has been deleted (nIndex == -1).
2213
             * We just ignore deleted fields
2214
             *--------------------------------------------------------*/
2215
19.4k
            if (pasFieldDef[iField].nIndex > 0)
2216
12.9k
                iField++;
2217
19.4k
        }
2218
2219
4.24k
        AVCRawBinClose(hFile);
2220
4.24k
    }
2221
20
    else
2222
20
    {
2223
20
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s",
2224
20
                 pszFname);
2225
20
        CPLFree(pszFname);
2226
20
        return nullptr;
2227
20
    }
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.24k
    if (sTableDef.numRecords > 0 &&
2235
4.14k
        AVCFileExists(pszInfoPath, sTableDef.szDataFile))
2236
2.91k
    {
2237
2.91k
        VSIStatBufL sStatBuf;
2238
2239
2.91k
        snprintf(pszFname, nFnameLen, "%s%s", pszInfoPath,
2240
2.91k
                 sTableDef.szDataFile);
2241
2.91k
        AVCAdjustCaseSensitiveFilename(pszFname);
2242
2243
2.91k
        hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType),
2244
2.91k
                              psDBCSInfo);
2245
2246
        /* OOPS... data file does not exist!
2247
         */
2248
2.91k
        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
2.91k
        if (VSIStatL(pszFname, &sStatBuf) != -1 && sTableDef.nRecSize > 0 &&
2267
704
            sStatBuf.st_size / sTableDef.nRecSize != sTableDef.numRecords)
2268
616
        {
2269
616
            sTableDef.numRecords = (int)(sStatBuf.st_size / sTableDef.nRecSize);
2270
616
        }
2271
2.91k
    }
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.24k
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
2282
2283
4.24k
    psFile->psRawBinFile = hFile;
2284
4.24k
    psFile->eCoverType = AVCCoverV7;
2285
4.24k
    psFile->eFileType = AVCFileTABLE;
2286
4.24k
    psFile->pszFilename = pszFname;
2287
2288
4.24k
    psFile->hdr.psTableDef = (AVCTableDef *)CPLMalloc(sizeof(AVCTableDef));
2289
4.24k
    *(psFile->hdr.psTableDef) = sTableDef;
2290
2291
4.24k
    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.24k
    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.24k
    psFile->cur.pasFields =
2303
4.24k
        (AVCField *)CPLCalloc(sTableDef.numFields, sizeof(AVCField));
2304
2305
16.6k
    for (i = 0; i < sTableDef.numFields; i++)
2306
12.4k
    {
2307
12.4k
        if (pasFieldDef[i].nType1 * 10 == AVC_FT_DATE ||
2308
12.2k
            pasFieldDef[i].nType1 * 10 == AVC_FT_CHAR ||
2309
10.2k
            pasFieldDef[i].nType1 * 10 == AVC_FT_FIXINT ||
2310
9.40k
            pasFieldDef[i].nType1 * 10 == AVC_FT_FIXNUM)
2311
8.92k
        {
2312
8.92k
            psFile->cur.pasFields[i].pszStr =
2313
8.92k
                (GByte *)CPLCalloc(pasFieldDef[i].nSize + 1, sizeof(char));
2314
8.92k
        }
2315
12.4k
    }
2316
2317
4.24k
    return psFile;
2318
4.24k
}
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
76.8k
{
2339
76.8k
    int i, nType, nBytesRead = 0;
2340
2341
76.8k
    if (psFile == nullptr)
2342
0
        return -1;
2343
2344
350k
    for (i = 0; i < nFields; i++)
2345
277k
    {
2346
277k
        if (AVCRawBinEOF(psFile))
2347
618
            return -1;
2348
2349
276k
        nType = pasDef[i].nType1 * 10;
2350
2351
276k
        if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR ||
2352
206k
            nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM)
2353
258k
        {
2354
            /*---------------------------------------------------------
2355
             * Values stored as strings
2356
             *--------------------------------------------------------*/
2357
258k
            AVCRawBinReadString(psFile, pasDef[i].nSize, pasFields[i].pszStr);
2358
258k
            pasFields[i].pszStr[pasDef[i].nSize] = '\0';
2359
258k
        }
2360
18.2k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4)
2361
7.73k
        {
2362
            /*---------------------------------------------------------
2363
             * 32 bit binary integers
2364
             *--------------------------------------------------------*/
2365
7.73k
            pasFields[i].nInt32 = AVCRawBinReadInt32(psFile);
2366
7.73k
        }
2367
10.4k
        else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2)
2368
257
        {
2369
            /*---------------------------------------------------------
2370
             * 16 bit binary integers
2371
             *--------------------------------------------------------*/
2372
257
            pasFields[i].nInt16 = AVCRawBinReadInt16(psFile);
2373
257
        }
2374
10.2k
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4)
2375
7.62k
        {
2376
            /*---------------------------------------------------------
2377
             * Single precision floats
2378
             *--------------------------------------------------------*/
2379
7.62k
            pasFields[i].fFloat = AVCRawBinReadFloat(psFile);
2380
7.62k
        }
2381
2.59k
        else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8)
2382
296
        {
2383
            /*---------------------------------------------------------
2384
             * Double precision floats
2385
             *--------------------------------------------------------*/
2386
296
            pasFields[i].dDouble = AVCRawBinReadDouble(psFile);
2387
296
        }
2388
2.30k
        else
2389
2.30k
        {
2390
            /*---------------------------------------------------------
2391
             * Hummm... unsupported field type...
2392
             *--------------------------------------------------------*/
2393
2.30k
            CPLError(CE_Failure, CPLE_NotSupported,
2394
2.30k
                     "Unsupported field type: (type=%d, size=%d)", nType,
2395
2.30k
                     pasDef[i].nSize);
2396
2.30k
            return -1;
2397
2.30k
        }
2398
2399
274k
        nBytesRead += pasDef[i].nSize;
2400
274k
    }
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
73.9k
    if (nBytesRead < nRecordSize)
2408
403
        AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR);
2409
2410
73.9k
    return 0;
2411
76.8k
}
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
267
{
2437
267
    AVCBinFile *psFile;
2438
267
    DBFHandle hDBFFile = nullptr;
2439
267
    int iField;
2440
267
    AVCTableDef *psTableDef;
2441
267
    AVCFieldInfo *pasFieldDef;
2442
2443
    /*-----------------------------------------------------------------
2444
     * Try to open the DBF file
2445
     *----------------------------------------------------------------*/
2446
267
    if ((hDBFFile = DBFOpen(pszDBFFilename, "rb")) == nullptr)
2447
78
    {
2448
78
        CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open table %s",
2449
78
                 pszDBFFilename);
2450
78
        return nullptr;
2451
78
    }
2452
2453
    /*-----------------------------------------------------------------
2454
     * Alloc. and init. the AVCBinFile structure.
2455
     *----------------------------------------------------------------*/
2456
189
    psFile = (AVCBinFile *)CPLCalloc(1, sizeof(AVCBinFile));
2457
2458
189
    psFile->hDBFFile = hDBFFile;
2459
2460
189
    psFile->eCoverType = AVCCoverPC;
2461
189
    psFile->eFileType = AVCFileTABLE;
2462
189
    psFile->pszFilename = CPLStrdup(pszDBFFilename);
2463
2464
189
    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
189
    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
189
    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
189
    psTableDef = (AVCTableDef *)CPLCalloc(1, sizeof(AVCTableDef));
2484
189
    psFile->hdr.psTableDef = psTableDef;
2485
2486
189
    snprintf(psTableDef->szTableName, sizeof(psTableDef->szTableName),
2487
189
             "%-32.32s", pszArcInfoTableName);
2488
2489
189
    psTableDef->numFields = (GInt16)DBFGetFieldCount(hDBFFile);
2490
2491
    /* We'll compute nRecSize value when we read fields info later */
2492
189
    psTableDef->nRecSize = 0;
2493
2494
189
    psTableDef->numRecords = DBFGetRecordCount(hDBFFile);
2495
2496
    /* All DBF tables are considered External */
2497
189
    strcpy(psTableDef->szExternal, "XX");
2498
2499
    /*-----------------------------------------------------------------
2500
     * Build Field definitions
2501
     *----------------------------------------------------------------*/
2502
189
    pasFieldDef =
2503
189
        (AVCFieldInfo *)CPLCalloc(psTableDef->numFields, sizeof(AVCFieldInfo));
2504
2505
189
    psTableDef->pasFieldDef = pasFieldDef;
2506
2507
9.81k
    for (iField = 0; iField < psTableDef->numFields; iField++)
2508
9.63k
    {
2509
9.63k
        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.63k
        DBFGetFieldInfo(hDBFFile, iField, pasFieldDef[iField].szName, &nWidth,
2519
9.63k
                        &nDecimals);
2520
9.63k
        const char cNativeType = DBFGetNativeFieldType(hDBFFile, iField);
2521
2522
9.63k
        pasFieldDef[iField].nFmtWidth = (GInt16)nWidth;
2523
9.63k
        pasFieldDef[iField].nFmtPrec = (GInt16)nDecimals;
2524
2525
        /* nIndex is the 1-based field index that we see in the E00 header */
2526
9.63k
        pasFieldDef[iField].nIndex = (GInt16)(iField + 1);
2527
2528
9.63k
        if (cNativeType == 'F' || (cNativeType == 'N' && nDecimals > 0))
2529
767
        {
2530
            /*---------------------------------------------------------
2531
             * BINARY FLOAT
2532
             *--------------------------------------------------------*/
2533
767
            pasFieldDef[iField].nType1 = AVC_FT_BINFLOAT / 10;
2534
767
            pasFieldDef[iField].nSize = 4;
2535
767
            pasFieldDef[iField].nFmtWidth = 12; /* PC Arc/Info ignores the */
2536
767
            pasFieldDef[iField].nFmtPrec = 3;   /* DBF width/precision     */
2537
767
        }
2538
8.86k
        else if (cNativeType == 'N')
2539
176
        {
2540
            /*---------------------------------------------------------
2541
             * BINARY INTEGER
2542
             *--------------------------------------------------------*/
2543
176
            pasFieldDef[iField].nType1 = AVC_FT_BININT / 10;
2544
176
            pasFieldDef[iField].nSize = 4;
2545
176
            pasFieldDef[iField].nFmtWidth = 5; /* PC Arc/Info ignores the */
2546
176
            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
176
            _AVCBinReadRepairDBFFieldName(pasFieldDef[iField].szName);
2553
176
        }
2554
8.68k
        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
8.27k
        else /* (cNativeType == 'C' || cNativeType == 'L') */
2564
8.27k
        {
2565
            /*---------------------------------------------------------
2566
             * CHAR STRINGS ... and all unknown types also handled as strings
2567
             *--------------------------------------------------------*/
2568
8.27k
            pasFieldDef[iField].nType1 = AVC_FT_CHAR / 10;
2569
8.27k
            pasFieldDef[iField].nSize = (GInt16)nWidth;
2570
8.27k
            pasFieldDef[iField].nFmtPrec = -1;
2571
8.27k
        }
2572
2573
        /*---------------------------------------------------------
2574
         * Keep track of position of field in record... first one always
2575
         * starts at offset=1
2576
         *--------------------------------------------------------*/
2577
9.63k
        if (iField == 0)
2578
183
            pasFieldDef[iField].nOffset = 1;
2579
9.44k
        else
2580
9.44k
            pasFieldDef[iField].nOffset = (pasFieldDef[iField - 1].nOffset +
2581
9.44k
                                           pasFieldDef[iField - 1].nSize);
2582
2583
        /*---------------------------------------------------------
2584
         * Set default values for all other unused members in the struct
2585
         *--------------------------------------------------------*/
2586
9.63k
        pasFieldDef[iField].v2 = -1;    /* Always -1 ? */
2587
9.63k
        pasFieldDef[iField].v4 = 4;     /* Always 4 ?  */
2588
9.63k
        pasFieldDef[iField].v5 = -1;    /* Always -1 ? */
2589
9.63k
        pasFieldDef[iField].nType2 = 0; /* Always 0 ?  */
2590
9.63k
        pasFieldDef[iField].v10 = -1;   /* Always -1 ? */
2591
9.63k
        pasFieldDef[iField].v11 = -1;   /* Always -1 ? */
2592
9.63k
        pasFieldDef[iField].v12 = -1;   /* Always -1 ? */
2593
9.63k
        pasFieldDef[iField].v13 = -1;   /* Always -1 ? */
2594
9.63k
    }
2595
2596
    /*-----------------------------------------------------------------
2597
     * Compute record size...
2598
     * Record size has to be rounded to a multiple of 2 bytes.
2599
     *----------------------------------------------------------------*/
2600
189
    if (psTableDef->numFields > 0)
2601
183
    {
2602
183
        psTableDef->nRecSize =
2603
183
            (pasFieldDef[psTableDef->numFields - 1].nOffset - 1 +
2604
183
             pasFieldDef[psTableDef->numFields - 1].nSize);
2605
183
        psTableDef->nRecSize = ((psTableDef->nRecSize + 1) / 2) * 2;
2606
183
    }
2607
6
    else
2608
6
        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
189
    psFile->cur.pasFields =
2615
189
        (AVCField *)CPLCalloc(psTableDef->numFields, sizeof(AVCField));
2616
2617
9.81k
    for (iField = 0; iField < psTableDef->numFields; iField++)
2618
9.63k
    {
2619
9.63k
        if (pasFieldDef[iField].nType1 * 10 == AVC_FT_DATE ||
2620
9.22k
            pasFieldDef[iField].nType1 * 10 == AVC_FT_CHAR ||
2621
943
            pasFieldDef[iField].nType1 * 10 == AVC_FT_FIXINT ||
2622
943
            pasFieldDef[iField].nType1 * 10 == AVC_FT_FIXNUM)
2623
8.68k
        {
2624
8.68k
            psFile->cur.pasFields[iField].pszStr = (GByte *)CPLCalloc(
2625
8.68k
                pasFieldDef[iField].nSize + 1, sizeof(GByte));
2626
8.68k
        }
2627
9.63k
    }
2628
2629
189
    return psFile;
2630
267
}
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
176
{
2743
176
    char *pszTmp;
2744
2745
176
    if ((pszTmp = strrchr(pszFieldName, '_')) == nullptr)
2746
98
        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
78
    if (EQUAL(pszTmp, "_"))
2755
26
        *pszTmp = '#';
2756
52
    else if (EQUAL(pszTmp, "_ID"))
2757
14
        *pszTmp = '-';
2758
78
}