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