Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/dgn/dgnread.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Microstation DGN Access Library
4
 * Purpose:  DGN Access Library element reading code.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "dgnlibp.h"
14
15
#include <algorithm>
16
17
static DGNElemCore *DGNParseTCB(DGNInfo *);
18
static DGNElemCore *DGNParseColorTable(DGNInfo *);
19
static DGNElemCore *DGNParseTagSet(DGNInfo *);
20
21
/************************************************************************/
22
/*                             DGN_INT16()                              */
23
/************************************************************************/
24
25
static short int DGN_INT16(const GByte *p)
26
1.55M
{
27
1.55M
    return static_cast<short>(p[0] | (p[1] << 8));
28
1.55M
}
29
30
/************************************************************************/
31
/*                           DGNGotoElement()                           */
32
/************************************************************************/
33
34
/**
35
 * Seek to indicated element.
36
 *
37
 * Changes what element will be read on the next call to DGNReadElement().
38
 * Note that this function requires and index, and one will be built if
39
 * not already available.
40
 *
41
 * @param hDGN the file to affect.
42
 * @param element_id the element to seek to.  These values are sequentially
43
 * ordered starting at zero for the first element.
44
 *
45
 * @return returns TRUE on success or FALSE on failure.
46
 */
47
48
int DGNGotoElement(DGNHandle hDGN, int element_id)
49
50
33
{
51
33
    DGNInfo *psDGN = (DGNInfo *)hDGN;
52
53
33
    DGNBuildIndex(psDGN);
54
55
33
    if (element_id < 0 || element_id >= psDGN->element_count)
56
0
        return FALSE;
57
58
33
    if (VSIFSeekL(psDGN->fp, psDGN->element_index[element_id].offset,
59
33
                  SEEK_SET) != 0)
60
0
        return FALSE;
61
62
33
    psDGN->next_element_id = element_id;
63
33
    psDGN->in_complex_group = false;
64
65
33
    return TRUE;
66
33
}
67
68
/************************************************************************/
69
/*                         DGNLoadRawElement()                          */
70
/************************************************************************/
71
72
int DGNLoadRawElement(DGNInfo *psDGN, int *pnType, int *pnLevel)
73
74
300M
{
75
    /* -------------------------------------------------------------------- */
76
    /*      Read the first four bytes to get the level, type, and word      */
77
    /*      count.                                                          */
78
    /* -------------------------------------------------------------------- */
79
300M
    if (VSIFReadL(psDGN->abyElem, 1, 4, psDGN->fp) != 4)
80
291M
        return FALSE;
81
82
    /* Is this an 0xFFFF endof file marker? */
83
8.48M
    if (psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff)
84
4.28k
        return FALSE;
85
86
8.47M
    int nWords = psDGN->abyElem[2] + psDGN->abyElem[3] * 256;
87
8.47M
    int nType = psDGN->abyElem[1] & 0x7f;
88
8.47M
    int nLevel = psDGN->abyElem[0] & 0x3f;
89
90
    /* -------------------------------------------------------------------- */
91
    /*      Read the rest of the element data into the working buffer.      */
92
    /* -------------------------------------------------------------------- */
93
8.47M
    if (nWords * 2 + 4 >= (int)sizeof(psDGN->abyElem))
94
0
        return FALSE;
95
96
    /* coverity[tainted_data] */
97
8.47M
    if ((int)VSIFReadL(psDGN->abyElem + 4, 2, nWords, psDGN->fp) != nWords)
98
7.43k
        return FALSE;
99
8.47M
    psDGN->abyElem[4 + 2 * nWords] = 0;
100
8.47M
    psDGN->abyElem[sizeof(psDGN->abyElem) - 1] = 0;
101
102
8.47M
    psDGN->nElemBytes = nWords * 2 + 4;
103
104
8.47M
    psDGN->next_element_id++;
105
106
    /* -------------------------------------------------------------------- */
107
    /*      Return requested info.                                          */
108
    /* -------------------------------------------------------------------- */
109
8.47M
    if (pnType != nullptr)
110
8.47M
        *pnType = nType;
111
112
8.47M
    if (pnLevel != nullptr)
113
8.47M
        *pnLevel = nLevel;
114
115
8.47M
    return TRUE;
116
8.47M
}
117
118
/************************************************************************/
119
/*                          DGNGetRawExtents()                          */
120
/*                                                                      */
121
/*      Returns false if the element type does not have recognizable    */
122
/*      element extents, other true and the extents will be updated.    */
123
/*                                                                      */
124
/*      It is assumed the raw element data has been loaded into the     */
125
/*      working area by DGNLoadRawElement().                            */
126
/************************************************************************/
127
128
static bool DGNGetRawExtents(DGNInfo *psDGN, int nType,
129
                             unsigned char *pabyRawData, GUInt32 *pnXMin,
130
                             GUInt32 *pnYMin, GUInt32 *pnZMin, GUInt32 *pnXMax,
131
                             GUInt32 *pnYMax, GUInt32 *pnZMax)
132
133
3.95M
{
134
3.95M
    if (pabyRawData == nullptr)
135
3.95M
        pabyRawData = psDGN->abyElem + 0;
136
137
3.95M
    switch (nType)
138
3.95M
    {
139
233k
        case DGNT_LINE:
140
268k
        case DGNT_LINE_STRING:
141
271k
        case DGNT_SHAPE:
142
280k
        case DGNT_CURVE:
143
296k
        case DGNT_BSPLINE_POLE:
144
297k
        case DGNT_BSPLINE_SURFACE_HEADER:
145
299k
        case DGNT_BSPLINE_CURVE_HEADER:
146
354k
        case DGNT_ELLIPSE:
147
531k
        case DGNT_ARC:
148
584k
        case DGNT_TEXT:
149
599k
        case DGNT_TEXT_NODE:
150
732k
        case DGNT_COMPLEX_CHAIN_HEADER:
151
887k
        case DGNT_COMPLEX_SHAPE_HEADER:
152
891k
        case DGNT_CONE:
153
892k
        case DGNT_3DSURFACE_HEADER:
154
945k
        case DGNT_3DSOLID_HEADER:
155
945k
            *pnXMin = DGN_INT32(pabyRawData + 4);
156
945k
            *pnYMin = DGN_INT32(pabyRawData + 8);
157
945k
            if (pnZMin != nullptr)
158
945k
                *pnZMin = DGN_INT32(pabyRawData + 12);
159
160
945k
            *pnXMax = DGN_INT32(pabyRawData + 16);
161
945k
            *pnYMax = DGN_INT32(pabyRawData + 20);
162
945k
            if (pnZMax != nullptr)
163
945k
                *pnZMax = DGN_INT32(pabyRawData + 24);
164
945k
            return true;
165
166
3.00M
        default:
167
3.00M
            return false;
168
3.95M
    }
169
3.95M
}
170
171
/************************************************************************/
172
/*                        DGNGetElementExtents()                        */
173
/************************************************************************/
174
175
/**
176
 * Fetch extents of an element.
177
 *
178
 * This function will return the extents of the passed element if possible.
179
 * The extents are extracted from the element header if it contains them,
180
 * and transformed into master georeferenced format.  Some element types
181
 * do not have extents at all and will fail.
182
 *
183
 * This call will also fail if the extents raw data for the element is not
184
 * available.  This will occur if it was not the most recently read element,
185
 * and if the raw_data field is not loaded.
186
 *
187
 * @param hDGN the handle of the file to read from.
188
 *
189
 * @param psElement the element to extract extents from.
190
 *
191
 * @param psMin structure loaded with X, Y and Z minimum values for the
192
 * extent.
193
 *
194
 * @param psMax structure loaded with X, Y and Z maximum values for the
195
 * extent.
196
 *
197
 * @return TRUE on success of FALSE if extracting extents fails.
198
 */
199
200
int DGNGetElementExtents(DGNHandle hDGN, DGNElemCore *psElement,
201
                         DGNPoint *psMin, DGNPoint *psMax)
202
203
0
{
204
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
205
0
    bool bResult = false;
206
207
0
    GUInt32 anMin[3] = {0, 0, 0};
208
0
    GUInt32 anMax[3] = {0, 0, 0};
209
210
    /* -------------------------------------------------------------------- */
211
    /*      Get the extents if we have raw data in the element, or          */
212
    /*      loaded in the file buffer.                                      */
213
    /* -------------------------------------------------------------------- */
214
0
    if (psElement->raw_data != nullptr)
215
0
        bResult = DGNGetRawExtents(psDGN, psElement->type, psElement->raw_data,
216
0
                                   anMin + 0, anMin + 1, anMin + 2, anMax + 0,
217
0
                                   anMax + 1, anMax + 2);
218
0
    else if (psElement->element_id == psDGN->next_element_id - 1)
219
0
        bResult = DGNGetRawExtents(psDGN, psElement->type, psDGN->abyElem + 0,
220
0
                                   anMin + 0, anMin + 1, anMin + 2, anMax + 0,
221
0
                                   anMax + 1, anMax + 2);
222
0
    else
223
0
    {
224
0
        CPLError(CE_Warning, CPLE_AppDefined,
225
0
                 "DGNGetElementExtents() fails because the requested element "
226
0
                 "does not have raw data available.");
227
0
        return FALSE;
228
0
    }
229
230
0
    if (!bResult)
231
0
        return FALSE;
232
233
    /* -------------------------------------------------------------------- */
234
    /*      Transform to user coordinate system and return.  The offset     */
235
    /*      is to convert from "binary offset" form to twos complement.     */
236
    /* -------------------------------------------------------------------- */
237
0
    psMin->x = anMin[0] - 2147483648.0;
238
0
    psMin->y = anMin[1] - 2147483648.0;
239
0
    psMin->z = anMin[2] - 2147483648.0;
240
241
0
    psMax->x = anMax[0] - 2147483648.0;
242
0
    psMax->y = anMax[1] - 2147483648.0;
243
0
    psMax->z = anMax[2] - 2147483648.0;
244
245
0
    DGNTransformPoint(psDGN, psMin);
246
0
    DGNTransformPoint(psDGN, psMax);
247
248
0
    return TRUE;
249
0
}
250
251
/************************************************************************/
252
/*                         DGNProcessElement()                          */
253
/*                                                                      */
254
/*      Assumes the raw element data has already been loaded, and       */
255
/*      tries to convert it into an element structure.                  */
256
/************************************************************************/
257
258
static DGNElemCore *DGNProcessElement(DGNInfo *psDGN, int nType, int nLevel)
259
260
4.40M
{
261
4.40M
    DGNElemCore *psElement = nullptr;
262
263
    /* -------------------------------------------------------------------- */
264
    /*      Handle based on element type.                                   */
265
    /* -------------------------------------------------------------------- */
266
4.40M
    switch (nType)
267
4.40M
    {
268
90.7k
        case DGNT_CELL_HEADER:
269
90.7k
        {
270
90.7k
            DGNElemCellHeader *psCell = static_cast<DGNElemCellHeader *>(
271
90.7k
                CPLCalloc(sizeof(DGNElemCellHeader), 1));
272
90.7k
            psElement = reinterpret_cast<DGNElemCore *>(psCell);
273
90.7k
            psElement->stype = DGNST_CELL_HEADER;
274
90.7k
            DGNParseCore(psDGN, psElement);
275
276
90.7k
            psCell->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
277
278
90.7k
            DGNRad50ToAscii(psDGN->abyElem[38] + psDGN->abyElem[39] * 256,
279
90.7k
                            psCell->name + 0);
280
90.7k
            DGNRad50ToAscii(psDGN->abyElem[40] + psDGN->abyElem[41] * 256,
281
90.7k
                            psCell->name + 3);
282
283
90.7k
            psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
284
90.7k
            psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
285
90.7k
            psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
286
90.7k
            psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256;
287
90.7k
            psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256;
288
289
90.7k
            if (psDGN->dimension == 2)
290
73.2k
            {
291
73.2k
                psCell->rnglow.x = DGN_INT32(psDGN->abyElem + 52);
292
73.2k
                psCell->rnglow.y = DGN_INT32(psDGN->abyElem + 56);
293
73.2k
                psCell->rnghigh.x = DGN_INT32(psDGN->abyElem + 60);
294
73.2k
                psCell->rnghigh.y = DGN_INT32(psDGN->abyElem + 64);
295
296
73.2k
                psCell->trans[0] =
297
73.2k
                    1.0 * DGN_INT32(psDGN->abyElem + 68) / (1U << 31);
298
73.2k
                psCell->trans[1] =
299
73.2k
                    1.0 * DGN_INT32(psDGN->abyElem + 72) / (1U << 31);
300
73.2k
                psCell->trans[2] =
301
73.2k
                    1.0 * DGN_INT32(psDGN->abyElem + 76) / (1U << 31);
302
73.2k
                psCell->trans[3] =
303
73.2k
                    1.0 * DGN_INT32(psDGN->abyElem + 80) / (1U << 31);
304
305
73.2k
                psCell->origin.x = DGN_INT32(psDGN->abyElem + 84);
306
73.2k
                psCell->origin.y = DGN_INT32(psDGN->abyElem + 88);
307
308
73.2k
                {
309
73.2k
                    const double a = DGN_INT32(psDGN->abyElem + 68);
310
73.2k
                    const double b = DGN_INT32(psDGN->abyElem + 72);
311
73.2k
                    const double c = DGN_INT32(psDGN->abyElem + 76);
312
73.2k
                    const double d = DGN_INT32(psDGN->abyElem + 80);
313
73.2k
                    const double a2 = a * a;
314
73.2k
                    const double c2 = c * c;
315
316
73.2k
                    psCell->xscale = sqrt(a2 + c2) / 214748;
317
73.2k
                    psCell->yscale = sqrt(b * b + d * d) / 214748;
318
73.2k
                    if ((a2 + c2) <= 0.0)
319
2.16k
                        psCell->rotation = 0.0;
320
71.1k
                    else
321
71.1k
                        psCell->rotation = acos(a / sqrt(a2 + c2));
322
323
73.2k
                    if (b <= 0)
324
4.35k
                        psCell->rotation = psCell->rotation * 180 / M_PI;
325
68.9k
                    else
326
68.9k
                        psCell->rotation = 360 - psCell->rotation * 180 / M_PI;
327
73.2k
                }
328
73.2k
            }
329
17.4k
            else
330
17.4k
            {
331
17.4k
                psCell->rnglow.x = DGN_INT32(psDGN->abyElem + 52);
332
17.4k
                psCell->rnglow.y = DGN_INT32(psDGN->abyElem + 56);
333
17.4k
                psCell->rnglow.z = DGN_INT32(psDGN->abyElem + 60);
334
17.4k
                psCell->rnghigh.x = DGN_INT32(psDGN->abyElem + 64);
335
17.4k
                psCell->rnghigh.y = DGN_INT32(psDGN->abyElem + 68);
336
17.4k
                psCell->rnghigh.z = DGN_INT32(psDGN->abyElem + 72);
337
338
17.4k
                psCell->trans[0] =
339
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 76) / (1U << 31);
340
17.4k
                psCell->trans[1] =
341
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 80) / (1U << 31);
342
17.4k
                psCell->trans[2] =
343
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 84) / (1U << 31);
344
17.4k
                psCell->trans[3] =
345
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 88) / (1U << 31);
346
17.4k
                psCell->trans[4] =
347
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 92) / (1U << 31);
348
17.4k
                psCell->trans[5] =
349
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 96) / (1U << 31);
350
17.4k
                psCell->trans[6] =
351
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 100) / (1U << 31);
352
17.4k
                psCell->trans[7] =
353
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 104) / (1U << 31);
354
17.4k
                psCell->trans[8] =
355
17.4k
                    1.0 * DGN_INT32(psDGN->abyElem + 108) / (1U << 31);
356
357
17.4k
                psCell->origin.x = DGN_INT32(psDGN->abyElem + 112);
358
17.4k
                psCell->origin.y = DGN_INT32(psDGN->abyElem + 116);
359
17.4k
                psCell->origin.z = DGN_INT32(psDGN->abyElem + 120);
360
17.4k
            }
361
362
90.7k
            DGNTransformPoint(psDGN, &(psCell->rnglow));
363
90.7k
            DGNTransformPoint(psDGN, &(psCell->rnghigh));
364
90.7k
            DGNTransformPoint(psDGN, &(psCell->origin));
365
90.7k
        }
366
90.7k
        break;
367
368
260k
        case DGNT_CELL_LIBRARY:
369
260k
        {
370
260k
            DGNElemCellLibrary *psCell = static_cast<DGNElemCellLibrary *>(
371
260k
                CPLCalloc(sizeof(DGNElemCellLibrary), 1));
372
260k
            psElement = reinterpret_cast<DGNElemCore *>(psCell);
373
260k
            psElement->stype = DGNST_CELL_LIBRARY;
374
260k
            DGNParseCore(psDGN, psElement);
375
376
260k
            DGNRad50ToAscii(psDGN->abyElem[32] + psDGN->abyElem[33] * 256,
377
260k
                            psCell->name + 0);
378
260k
            DGNRad50ToAscii(psDGN->abyElem[34] + psDGN->abyElem[35] * 256,
379
260k
                            psCell->name + 3);
380
381
260k
            psElement->properties =
382
260k
                psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
383
384
260k
            psCell->dispsymb = psDGN->abyElem[40] + psDGN->abyElem[41] * 256;
385
386
260k
            psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
387
260k
            psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
388
260k
            psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
389
260k
            psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256;
390
260k
            psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256;
391
392
260k
            psCell->numwords = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
393
394
260k
            memset(psCell->description, 0, sizeof(psCell->description));
395
396
2.60M
            for (int iWord = 0; iWord < 9; iWord++)
397
2.34M
            {
398
2.34M
                int iOffset = 52 + iWord * 2;
399
400
2.34M
                DGNRad50ToAscii(psDGN->abyElem[iOffset] +
401
2.34M
                                    psDGN->abyElem[iOffset + 1] * 256,
402
2.34M
                                psCell->description + iWord * 3);
403
2.34M
            }
404
260k
        }
405
260k
        break;
406
407
247k
        case DGNT_LINE:
408
247k
        {
409
247k
            DGNElemMultiPoint *psLine = static_cast<DGNElemMultiPoint *>(
410
247k
                CPLCalloc(sizeof(DGNElemMultiPoint) + sizeof(DGNPoint), 1));
411
247k
            psElement = reinterpret_cast<DGNElemCore *>(psLine);
412
247k
            psElement->stype = DGNST_MULTIPOINT;
413
247k
            DGNParseCore(psDGN, psElement);
414
415
247k
            int deltaLength = 0, deltaStart = 0;
416
247k
            if (psLine->core.properties & DGNPF_ATTRIBUTES)
417
804
            {
418
2.64M
                for (int iAttr = 0; iAttr < psLine->core.attr_bytes - 3;
419
2.64M
                     iAttr++)
420
2.64M
                {
421
2.64M
                    if (psLine->core.attr_data[iAttr] == 0xA9 &&
422
2.57k
                        psLine->core.attr_data[iAttr + 1] == 0x51)
423
16
                    {
424
16
                        deltaLength =
425
16
                            (psLine->core.attr_data[iAttr + 2] +
426
16
                             psLine->core.attr_data[iAttr + 3] * 256) *
427
16
                            2;
428
16
                        deltaStart = iAttr + 6;
429
16
                        break;
430
16
                    }
431
2.64M
                }
432
804
            }
433
434
247k
            psLine->num_vertices = 2;
435
247k
            if (psDGN->dimension == 2)
436
201k
            {
437
201k
                psLine->vertices[0].x = DGN_INT32(psDGN->abyElem + 36);
438
201k
                psLine->vertices[0].y = DGN_INT32(psDGN->abyElem + 40);
439
201k
                psLine->vertices[1].x = DGN_INT32(psDGN->abyElem + 44);
440
201k
                psLine->vertices[1].y = DGN_INT32(psDGN->abyElem + 48);
441
201k
            }
442
46.4k
            else
443
46.4k
            {
444
46.4k
                psLine->vertices[0].x = DGN_INT32(psDGN->abyElem + 36);
445
46.4k
                psLine->vertices[0].y = DGN_INT32(psDGN->abyElem + 40);
446
46.4k
                psLine->vertices[0].z = DGN_INT32(psDGN->abyElem + 44);
447
46.4k
                psLine->vertices[1].x = DGN_INT32(psDGN->abyElem + 48);
448
46.4k
                psLine->vertices[1].y = DGN_INT32(psDGN->abyElem + 52);
449
46.4k
                psLine->vertices[1].z = DGN_INT32(psDGN->abyElem + 56);
450
46.4k
            }
451
452
247k
            if (deltaStart && deltaLength &&
453
16
                deltaStart + 1 * 4 + 2 + 2 <= psLine->core.attr_bytes)
454
15
            {
455
45
                for (int i = 0; i < 2; i++)
456
30
                {
457
30
                    int dx =
458
30
                        DGN_INT16(psLine->core.attr_data + deltaStart + i * 4);
459
30
                    int dy = DGN_INT16(psLine->core.attr_data + deltaStart +
460
30
                                       i * 4 + 2);
461
30
                    psLine->vertices[i].x += dx / 32767.0;
462
30
                    psLine->vertices[i].y += dy / 32767.0;
463
30
                }
464
15
            }
465
466
247k
            DGNTransformPoint(psDGN, psLine->vertices + 0);
467
247k
            DGNTransformPoint(psDGN, psLine->vertices + 1);
468
247k
        }
469
247k
        break;
470
471
44.4k
        case DGNT_LINE_STRING:
472
46.7k
        case DGNT_SHAPE:
473
61.6k
        case DGNT_CURVE:
474
85.5k
        case DGNT_BSPLINE_POLE:
475
85.5k
        {
476
85.5k
            int pntsize = psDGN->dimension * 4;
477
478
85.5k
            int count = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
479
85.5k
            if (count < 2)
480
32.7k
            {
481
32.7k
                CPLError(CE_Failure, CPLE_AssertionFailed, "count < 2");
482
32.7k
                return nullptr;
483
32.7k
            }
484
52.8k
            DGNElemMultiPoint *psLine =
485
52.8k
                static_cast<DGNElemMultiPoint *>(VSI_CALLOC_VERBOSE(
486
52.8k
                    sizeof(DGNElemMultiPoint) + (count - 1) * sizeof(DGNPoint),
487
52.8k
                    1));
488
52.8k
            if (psLine == nullptr)
489
0
                return nullptr;
490
52.8k
            psElement = reinterpret_cast<DGNElemCore *>(psLine);
491
52.8k
            psElement->stype = DGNST_MULTIPOINT;
492
52.8k
            DGNParseCore(psDGN, psElement);
493
494
52.8k
            if (psDGN->nElemBytes < 38 + count * pntsize)
495
52.6k
            {
496
52.6k
                int new_count = (psDGN->nElemBytes - 38) / pntsize;
497
52.6k
                if (new_count < 0)
498
34.9k
                {
499
34.9k
                    CPLError(CE_Failure, CPLE_AssertionFailed, "new_count < 2");
500
34.9k
                    DGNFreeElement(psDGN, psElement);
501
34.9k
                    return nullptr;
502
34.9k
                }
503
17.6k
                CPLError(CE_Warning, CPLE_AppDefined,
504
17.6k
                         "Trimming multipoint vertices to %d from %d because\n"
505
17.6k
                         "element is short.\n",
506
17.6k
                         new_count, count);
507
17.6k
                count = new_count;
508
17.6k
            }
509
17.9k
            int deltaLength = 0, deltaStart = 0;
510
17.9k
            if (psLine->core.properties & DGNPF_ATTRIBUTES)
511
12.0k
            {
512
4.70M
                for (int iAttr = 0; iAttr < psLine->core.attr_bytes - 3;
513
4.69M
                     iAttr++)
514
4.69M
                {
515
4.69M
                    if (psLine->core.attr_data[iAttr] == 0xA9 &&
516
8.10k
                        psLine->core.attr_data[iAttr + 1] == 0x51)
517
95
                    {
518
95
                        deltaLength =
519
95
                            (psLine->core.attr_data[iAttr + 2] +
520
95
                             psLine->core.attr_data[iAttr + 3] * 256) *
521
95
                            2;
522
95
                        deltaStart = iAttr + 6;
523
95
                        break;
524
95
                    }
525
4.69M
                }
526
12.0k
            }
527
7.81M
            for (int i = 0; i < count && ((psDGN->dimension == 3) ? 46 : 42) +
528
7.79M
                                                 i * pntsize + 4 <=
529
7.79M
                                             psDGN->nElemBytes;
530
7.79M
                 i++)
531
7.79M
            {
532
7.79M
                psLine->vertices[i].x =
533
7.79M
                    DGN_INT32(psDGN->abyElem + 38 + i * pntsize);
534
7.79M
                psLine->vertices[i].y =
535
7.79M
                    DGN_INT32(psDGN->abyElem + 42 + i * pntsize);
536
7.79M
                if (psDGN->dimension == 3)
537
367k
                    psLine->vertices[i].z =
538
367k
                        DGN_INT32(psDGN->abyElem + 46 + i * pntsize);
539
7.79M
                if (deltaStart && deltaLength &&
540
1.01M
                    deltaStart + i * 4 + 2 + 2 <= psLine->core.attr_bytes)
541
315k
                {
542
315k
                    int dx =
543
315k
                        DGN_INT16(psLine->core.attr_data + deltaStart + i * 4);
544
315k
                    int dy = DGN_INT16(psLine->core.attr_data + deltaStart +
545
315k
                                       i * 4 + 2);
546
315k
                    psLine->vertices[i].x += dx / 32767.0;
547
315k
                    psLine->vertices[i].y += dy / 32767.0;
548
315k
                }
549
7.79M
                DGNTransformPoint(psDGN, psLine->vertices + i);
550
7.79M
                psLine->num_vertices = i + 1;
551
7.79M
            }
552
17.9k
        }
553
0
        break;
554
555
16.2k
        case DGNT_TEXT_NODE:
556
16.2k
        {
557
16.2k
            DGNElemTextNode *psNode = static_cast<DGNElemTextNode *>(
558
16.2k
                CPLCalloc(sizeof(DGNElemTextNode), 1));
559
16.2k
            psElement = reinterpret_cast<DGNElemCore *>(psNode);
560
16.2k
            psElement->stype = DGNST_TEXT_NODE;
561
16.2k
            DGNParseCore(psDGN, psElement);
562
563
16.2k
            psNode->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
564
16.2k
            psNode->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
565
566
16.2k
            psNode->node_number = psDGN->abyElem[40] + psDGN->abyElem[41] * 256;
567
16.2k
            psNode->max_length = psDGN->abyElem[42];
568
16.2k
            psNode->max_used = psDGN->abyElem[43];
569
16.2k
            psNode->font_id = psDGN->abyElem[44];
570
16.2k
            psNode->justification = psDGN->abyElem[45];
571
16.2k
            psNode->length_mult =
572
16.2k
                (DGN_INT32(psDGN->abyElem + 50)) * psDGN->scale * 6.0 / 1000.0;
573
16.2k
            psNode->height_mult =
574
16.2k
                (DGN_INT32(psDGN->abyElem + 54)) * psDGN->scale * 6.0 / 1000.0;
575
576
16.2k
            if (psDGN->dimension == 2)
577
11.7k
            {
578
11.7k
                psNode->rotation = DGN_INT32(psDGN->abyElem + 58) / 360000.0;
579
580
11.7k
                psNode->origin.x = DGN_INT32(psDGN->abyElem + 62);
581
11.7k
                psNode->origin.y = DGN_INT32(psDGN->abyElem + 66);
582
11.7k
            }
583
4.44k
            else
584
4.44k
            {
585
                /* leave quaternion for later */
586
587
4.44k
                psNode->origin.x = DGN_INT32(psDGN->abyElem + 74);
588
4.44k
                psNode->origin.y = DGN_INT32(psDGN->abyElem + 78);
589
4.44k
                psNode->origin.z = DGN_INT32(psDGN->abyElem + 82);
590
4.44k
            }
591
16.2k
            DGNTransformPoint(psDGN, &(psNode->origin));
592
16.2k
        }
593
16.2k
        break;
594
595
15.0k
        case DGNT_GROUP_DATA:
596
15.0k
            if (nLevel == DGN_GDL_COLOR_TABLE)
597
8.85k
            {
598
8.85k
                psElement = DGNParseColorTable(psDGN);
599
8.85k
            }
600
6.18k
            else
601
6.18k
            {
602
6.18k
                psElement = static_cast<DGNElemCore *>(
603
6.18k
                    CPLCalloc(sizeof(DGNElemCore), 1));
604
6.18k
                psElement->stype = DGNST_CORE;
605
6.18k
                DGNParseCore(psDGN, psElement);
606
6.18k
            }
607
15.0k
            break;
608
609
59.3k
        case DGNT_ELLIPSE:
610
59.3k
        {
611
59.3k
            DGNElemArc *psEllipse =
612
59.3k
                static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
613
59.3k
            psElement = reinterpret_cast<DGNElemCore *>(psEllipse);
614
59.3k
            psElement->stype = DGNST_ARC;
615
59.3k
            DGNParseCore(psDGN, psElement);
616
617
59.3k
            memcpy(&(psEllipse->primary_axis), psDGN->abyElem + 36, 8);
618
59.3k
            DGN2IEEEDouble(&(psEllipse->primary_axis));
619
59.3k
            psEllipse->primary_axis *= psDGN->scale;
620
621
59.3k
            memcpy(&(psEllipse->secondary_axis), psDGN->abyElem + 44, 8);
622
59.3k
            DGN2IEEEDouble(&(psEllipse->secondary_axis));
623
59.3k
            psEllipse->secondary_axis *= psDGN->scale;
624
625
59.3k
            if (psDGN->dimension == 2)
626
48.9k
            {
627
48.9k
                psEllipse->rotation = DGN_INT32(psDGN->abyElem + 52);
628
48.9k
                psEllipse->rotation = psEllipse->rotation / 360000.0;
629
630
48.9k
                memcpy(&(psEllipse->origin.x), psDGN->abyElem + 56, 8);
631
48.9k
                DGN2IEEEDouble(&(psEllipse->origin.x));
632
633
48.9k
                memcpy(&(psEllipse->origin.y), psDGN->abyElem + 64, 8);
634
48.9k
                DGN2IEEEDouble(&(psEllipse->origin.y));
635
48.9k
            }
636
10.3k
            else
637
10.3k
            {
638
                /* leave quaternion for later */
639
640
10.3k
                memcpy(&(psEllipse->origin.x), psDGN->abyElem + 68, 8);
641
10.3k
                DGN2IEEEDouble(&(psEllipse->origin.x));
642
643
10.3k
                memcpy(&(psEllipse->origin.y), psDGN->abyElem + 76, 8);
644
10.3k
                DGN2IEEEDouble(&(psEllipse->origin.y));
645
646
10.3k
                memcpy(&(psEllipse->origin.z), psDGN->abyElem + 84, 8);
647
10.3k
                DGN2IEEEDouble(&(psEllipse->origin.z));
648
649
10.3k
                psEllipse->quat[0] = DGN_INT32(psDGN->abyElem + 52);
650
10.3k
                psEllipse->quat[1] = DGN_INT32(psDGN->abyElem + 56);
651
10.3k
                psEllipse->quat[2] = DGN_INT32(psDGN->abyElem + 60);
652
10.3k
                psEllipse->quat[3] = DGN_INT32(psDGN->abyElem + 64);
653
10.3k
            }
654
655
59.3k
            DGNTransformPoint(psDGN, &(psEllipse->origin));
656
657
59.3k
            psEllipse->startang = 0.0;
658
59.3k
            psEllipse->sweepang = 360.0;
659
59.3k
        }
660
59.3k
        break;
661
662
186k
        case DGNT_ARC:
663
186k
        {
664
186k
            GInt32 nSweepVal = 0;
665
666
186k
            DGNElemArc *psEllipse =
667
186k
                static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
668
186k
            psElement = reinterpret_cast<DGNElemCore *>(psEllipse);
669
186k
            psElement->stype = DGNST_ARC;
670
186k
            DGNParseCore(psDGN, psElement);
671
672
186k
            psEllipse->startang = DGN_INT32(psDGN->abyElem + 36);
673
186k
            psEllipse->startang = psEllipse->startang / 360000.0;
674
186k
            if (psDGN->abyElem[41] & 0x80)
675
1.25k
            {
676
1.25k
                psDGN->abyElem[41] &= 0x7f;
677
1.25k
                nSweepVal = -1 * DGN_INT32(psDGN->abyElem + 40);
678
1.25k
            }
679
185k
            else
680
185k
                nSweepVal = DGN_INT32(psDGN->abyElem + 40);
681
682
186k
            if (nSweepVal == 0)
683
51.5k
                psEllipse->sweepang = 360.0;
684
135k
            else
685
135k
                psEllipse->sweepang = nSweepVal / 360000.0;
686
687
186k
            memcpy(&(psEllipse->primary_axis), psDGN->abyElem + 44, 8);
688
186k
            DGN2IEEEDouble(&(psEllipse->primary_axis));
689
186k
            psEllipse->primary_axis *= psDGN->scale;
690
691
186k
            memcpy(&(psEllipse->secondary_axis), psDGN->abyElem + 52, 8);
692
186k
            DGN2IEEEDouble(&(psEllipse->secondary_axis));
693
186k
            psEllipse->secondary_axis *= psDGN->scale;
694
695
186k
            if (psDGN->dimension == 2)
696
164k
            {
697
164k
                psEllipse->rotation = DGN_INT32(psDGN->abyElem + 60);
698
164k
                psEllipse->rotation = psEllipse->rotation / 360000.0;
699
700
164k
                memcpy(&(psEllipse->origin.x), psDGN->abyElem + 64, 8);
701
164k
                DGN2IEEEDouble(&(psEllipse->origin.x));
702
703
164k
                memcpy(&(psEllipse->origin.y), psDGN->abyElem + 72, 8);
704
164k
                DGN2IEEEDouble(&(psEllipse->origin.y));
705
164k
            }
706
22.1k
            else
707
22.1k
            {
708
                /* for now we don't try to handle quaternion */
709
22.1k
                psEllipse->rotation = 0;
710
711
22.1k
                memcpy(&(psEllipse->origin.x), psDGN->abyElem + 76, 8);
712
22.1k
                DGN2IEEEDouble(&(psEllipse->origin.x));
713
714
22.1k
                memcpy(&(psEllipse->origin.y), psDGN->abyElem + 84, 8);
715
22.1k
                DGN2IEEEDouble(&(psEllipse->origin.y));
716
717
22.1k
                memcpy(&(psEllipse->origin.z), psDGN->abyElem + 92, 8);
718
22.1k
                DGN2IEEEDouble(&(psEllipse->origin.z));
719
720
22.1k
                psEllipse->quat[0] = DGN_INT32(psDGN->abyElem + 60);
721
22.1k
                psEllipse->quat[1] = DGN_INT32(psDGN->abyElem + 64);
722
22.1k
                psEllipse->quat[2] = DGN_INT32(psDGN->abyElem + 68);
723
22.1k
                psEllipse->quat[3] = DGN_INT32(psDGN->abyElem + 72);
724
22.1k
            }
725
726
186k
            DGNTransformPoint(psDGN, &(psEllipse->origin));
727
186k
        }
728
186k
        break;
729
730
55.5k
        case DGNT_TEXT:
731
55.5k
        {
732
55.5k
            int num_chars = 0;
733
55.5k
            int text_off = 0;
734
735
55.5k
            if (psDGN->dimension == 2)
736
52.2k
                num_chars = psDGN->abyElem[58];
737
3.33k
            else
738
3.33k
                num_chars = psDGN->abyElem[74];
739
740
55.5k
            DGNElemText *psText = static_cast<DGNElemText *>(
741
55.5k
                CPLCalloc(sizeof(DGNElemText) + num_chars, 1));
742
55.5k
            psElement = reinterpret_cast<DGNElemCore *>(psText);
743
55.5k
            psElement->stype = DGNST_TEXT;
744
55.5k
            DGNParseCore(psDGN, psElement);
745
746
55.5k
            psText->font_id = psDGN->abyElem[36];
747
55.5k
            psText->justification = psDGN->abyElem[37];
748
55.5k
            psText->length_mult =
749
55.5k
                (DGN_INT32(psDGN->abyElem + 38)) * psDGN->scale * 6.0 / 1000.0;
750
55.5k
            psText->height_mult =
751
55.5k
                (DGN_INT32(psDGN->abyElem + 42)) * psDGN->scale * 6.0 / 1000.0;
752
753
55.5k
            if (psDGN->dimension == 2)
754
52.2k
            {
755
52.2k
                psText->rotation = DGN_INT32(psDGN->abyElem + 46);
756
52.2k
                psText->rotation = psText->rotation / 360000.0;
757
758
52.2k
                psText->origin.x = DGN_INT32(psDGN->abyElem + 50);
759
52.2k
                psText->origin.y = DGN_INT32(psDGN->abyElem + 54);
760
52.2k
                text_off = 60;
761
52.2k
            }
762
3.33k
            else
763
3.33k
            {
764
                /* leave quaternion for later */
765
766
3.33k
                psText->origin.x = DGN_INT32(psDGN->abyElem + 62);
767
3.33k
                psText->origin.y = DGN_INT32(psDGN->abyElem + 66);
768
3.33k
                psText->origin.z = DGN_INT32(psDGN->abyElem + 70);
769
3.33k
                text_off = 76;
770
3.33k
            }
771
772
55.5k
            DGNTransformPoint(psDGN, &(psText->origin));
773
774
            /* experimental multibyte support from Ason Kang
775
             * (hiska@netian.com)*/
776
55.5k
            if (*(psDGN->abyElem + text_off) == 0xFF &&
777
9.49k
                *(psDGN->abyElem + text_off + 1) == 0xFD)
778
229
            {
779
229
                int n = 0;
780
17.8k
                for (int i = 0; i < num_chars / 2 - 1; i++)
781
17.5k
                {
782
17.5k
                    unsigned short w = 0;
783
17.5k
                    memcpy(&w, psDGN->abyElem + text_off + 2 + i * 2, 2);
784
17.5k
                    CPL_LSBPTR16(&w);
785
17.5k
                    if (w < 256)
786
10.4k
                    {  // if alpa-numeric code area : Normal character
787
10.4k
                        *(psText->string + n) = (char)(w & 0xFF);
788
10.4k
                        n++;  // skip 1 byte;
789
10.4k
                    }
790
7.12k
                    else
791
7.12k
                    {  // if extend code area : 2 byte Korean character
792
7.12k
                        *(psText->string + n) = (char)(w >> 8);        // hi
793
7.12k
                        *(psText->string + n + 1) = (char)(w & 0xFF);  // lo
794
7.12k
                        n += 2;                                        // 2 byte
795
7.12k
                    }
796
17.5k
                }
797
229
                psText->string[n] = '\0';  // terminate C string
798
229
            }
799
55.3k
            else
800
55.3k
            {
801
55.3k
                memcpy(psText->string, psDGN->abyElem + text_off, num_chars);
802
55.3k
                psText->string[num_chars] = '\0';
803
55.3k
            }
804
55.5k
        }
805
55.5k
        break;
806
807
52.8k
        case DGNT_TCB:
808
52.8k
            psElement = DGNParseTCB(psDGN);
809
52.8k
            break;
810
811
138k
        case DGNT_COMPLEX_CHAIN_HEADER:
812
313k
        case DGNT_COMPLEX_SHAPE_HEADER:
813
313k
        {
814
313k
            DGNElemComplexHeader *psHdr = static_cast<DGNElemComplexHeader *>(
815
313k
                CPLCalloc(sizeof(DGNElemComplexHeader), 1));
816
313k
            psElement = reinterpret_cast<DGNElemCore *>(psHdr);
817
313k
            psElement->stype = DGNST_COMPLEX_HEADER;
818
313k
            DGNParseCore(psDGN, psElement);
819
820
313k
            psHdr->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
821
313k
            psHdr->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
822
313k
        }
823
313k
        break;
824
825
12.2k
        case DGNT_TAG_VALUE:
826
12.2k
        {
827
12.2k
            DGNElemTagValue *psTag = static_cast<DGNElemTagValue *>(
828
12.2k
                CPLCalloc(sizeof(DGNElemTagValue), 1));
829
12.2k
            psElement = reinterpret_cast<DGNElemCore *>(psTag);
830
12.2k
            psElement->stype = DGNST_TAG_VALUE;
831
12.2k
            DGNParseCore(psDGN, psElement);
832
833
12.2k
            psTag->tagType = psDGN->abyElem[74] + psDGN->abyElem[75] * 256;
834
12.2k
            memcpy(&(psTag->tagSet), psDGN->abyElem + 68, 4);
835
12.2k
            CPL_LSBPTR32(&(psTag->tagSet));
836
12.2k
            psTag->tagIndex = psDGN->abyElem[72] + psDGN->abyElem[73] * 256;
837
12.2k
            psTag->tagLength = psDGN->abyElem[150] + psDGN->abyElem[151] * 256;
838
839
12.2k
            if (psTag->tagType == 1)
840
520
            {
841
520
                psTag->tagValue.string =
842
520
                    CPLStrdup((char *)psDGN->abyElem + 154);
843
520
            }
844
11.7k
            else if (psTag->tagType == 3)
845
278
            {
846
278
                memcpy(&(psTag->tagValue.integer), psDGN->abyElem + 154, 4);
847
278
                CPL_LSBPTR32(&(psTag->tagValue.integer));
848
278
            }
849
11.4k
            else if (psTag->tagType == 4)
850
25
            {
851
25
                memcpy(&(psTag->tagValue.real), psDGN->abyElem + 154, 8);
852
25
                DGN2IEEEDouble(&(psTag->tagValue.real));
853
25
            }
854
12.2k
        }
855
12.2k
        break;
856
857
9.00k
        case DGNT_APPLICATION_ELEM:
858
9.00k
            if (nLevel == 24)
859
1.68k
            {
860
1.68k
                psElement = DGNParseTagSet(psDGN);
861
1.68k
                if (psElement == nullptr)
862
728
                    return nullptr;
863
1.68k
            }
864
7.31k
            else
865
7.31k
            {
866
7.31k
                psElement = static_cast<DGNElemCore *>(
867
7.31k
                    CPLCalloc(sizeof(DGNElemCore), 1));
868
7.31k
                psElement->stype = DGNST_CORE;
869
7.31k
                DGNParseCore(psDGN, psElement);
870
7.31k
            }
871
8.27k
            break;
872
873
28.3k
        case DGNT_CONE:
874
28.3k
        {
875
28.3k
            if (psDGN->dimension != 3)
876
22.3k
            {
877
22.3k
                CPLError(CE_Failure, CPLE_AssertionFailed,
878
22.3k
                         "psDGN->dimension != 3");
879
22.3k
                return nullptr;
880
22.3k
            }
881
882
6.02k
            DGNElemCone *psCone =
883
6.02k
                static_cast<DGNElemCone *>(CPLCalloc(sizeof(DGNElemCone), 1));
884
6.02k
            psElement = reinterpret_cast<DGNElemCore *>(psCone);
885
6.02k
            psElement->stype = DGNST_CONE;
886
6.02k
            DGNParseCore(psDGN, psElement);
887
888
6.02k
            psCone->unknown = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
889
6.02k
            psCone->quat[0] = DGN_INT32(psDGN->abyElem + 38);
890
6.02k
            psCone->quat[1] = DGN_INT32(psDGN->abyElem + 42);
891
6.02k
            psCone->quat[2] = DGN_INT32(psDGN->abyElem + 46);
892
6.02k
            psCone->quat[3] = DGN_INT32(psDGN->abyElem + 50);
893
894
6.02k
            memcpy(&(psCone->center_1.x), psDGN->abyElem + 54, 8);
895
6.02k
            DGN2IEEEDouble(&(psCone->center_1.x));
896
6.02k
            memcpy(&(psCone->center_1.y), psDGN->abyElem + 62, 8);
897
6.02k
            DGN2IEEEDouble(&(psCone->center_1.y));
898
6.02k
            memcpy(&(psCone->center_1.z), psDGN->abyElem + 70, 8);
899
6.02k
            DGN2IEEEDouble(&(psCone->center_1.z));
900
6.02k
            memcpy(&(psCone->radius_1), psDGN->abyElem + 78, 8);
901
6.02k
            DGN2IEEEDouble(&(psCone->radius_1));
902
903
6.02k
            memcpy(&(psCone->center_2.x), psDGN->abyElem + 86, 8);
904
6.02k
            DGN2IEEEDouble(&(psCone->center_2.x));
905
6.02k
            memcpy(&(psCone->center_2.y), psDGN->abyElem + 94, 8);
906
6.02k
            DGN2IEEEDouble(&(psCone->center_2.y));
907
6.02k
            memcpy(&(psCone->center_2.z), psDGN->abyElem + 102, 8);
908
6.02k
            DGN2IEEEDouble(&(psCone->center_2.z));
909
6.02k
            memcpy(&(psCone->radius_2), psDGN->abyElem + 110, 8);
910
6.02k
            DGN2IEEEDouble(&(psCone->radius_2));
911
912
6.02k
            psCone->radius_1 *= psDGN->scale;
913
6.02k
            psCone->radius_2 *= psDGN->scale;
914
6.02k
            DGNTransformPoint(psDGN, &psCone->center_1);
915
6.02k
            DGNTransformPoint(psDGN, &psCone->center_2);
916
6.02k
        }
917
0
        break;
918
919
1.62k
        case DGNT_3DSURFACE_HEADER:
920
57.4k
        case DGNT_3DSOLID_HEADER:
921
57.4k
        {
922
57.4k
            DGNElemComplexHeader *psShape = static_cast<DGNElemComplexHeader *>(
923
57.4k
                CPLCalloc(sizeof(DGNElemComplexHeader), 1));
924
57.4k
            psElement = reinterpret_cast<DGNElemCore *>(psShape);
925
57.4k
            psElement->stype = DGNST_COMPLEX_HEADER;
926
57.4k
            DGNParseCore(psDGN, psElement);
927
928
            // Read complex header
929
57.4k
            psShape->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
930
57.4k
            psShape->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
931
57.4k
            psShape->surftype = psDGN->abyElem[40];
932
57.4k
            psShape->boundelms = psDGN->abyElem[41] + 1;
933
57.4k
        }
934
57.4k
        break;
935
10.3k
        case DGNT_BSPLINE_SURFACE_HEADER:
936
10.3k
        {
937
10.3k
            DGNElemBSplineSurfaceHeader *psSpline =
938
10.3k
                static_cast<DGNElemBSplineSurfaceHeader *>(
939
10.3k
                    CPLCalloc(sizeof(DGNElemBSplineSurfaceHeader), 1));
940
10.3k
            psElement = reinterpret_cast<DGNElemCore *>(psSpline);
941
10.3k
            psElement->stype = DGNST_BSPLINE_SURFACE_HEADER;
942
10.3k
            DGNParseCore(psDGN, psElement);
943
944
            // Read B-Spline surface header
945
10.3k
            psSpline->desc_words =
946
10.3k
                static_cast<long>(DGN_INT32(psDGN->abyElem + 36));
947
10.3k
            psSpline->curve_type = psDGN->abyElem[41];
948
949
            // U
950
10.3k
            psSpline->u_order = (psDGN->abyElem[40] & 0x0f) + 2;
951
10.3k
            psSpline->u_properties = psDGN->abyElem[40] & 0xf0;
952
10.3k
            psSpline->num_poles_u =
953
10.3k
                psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
954
10.3k
            psSpline->num_knots_u =
955
10.3k
                psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
956
10.3k
            psSpline->rule_lines_u =
957
10.3k
                psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
958
959
            // V
960
10.3k
            psSpline->v_order = (psDGN->abyElem[48] & 0x0f) + 2;
961
10.3k
            psSpline->v_properties = psDGN->abyElem[48] & 0xf0;
962
10.3k
            psSpline->num_poles_v =
963
10.3k
                psDGN->abyElem[50] + psDGN->abyElem[51] * 256;
964
10.3k
            psSpline->num_knots_v =
965
10.3k
                psDGN->abyElem[52] + psDGN->abyElem[53] * 256;
966
10.3k
            psSpline->rule_lines_v =
967
10.3k
                psDGN->abyElem[54] + psDGN->abyElem[55] * 256;
968
969
10.3k
            psSpline->num_bounds =
970
10.3k
                psDGN->abyElem[56] + psDGN->abyElem[57] * 556;
971
10.3k
        }
972
10.3k
        break;
973
2.26k
        case DGNT_BSPLINE_CURVE_HEADER:
974
2.26k
        {
975
2.26k
            DGNElemBSplineCurveHeader *psSpline =
976
2.26k
                static_cast<DGNElemBSplineCurveHeader *>(
977
2.26k
                    CPLCalloc(sizeof(DGNElemBSplineCurveHeader), 1));
978
2.26k
            psElement = reinterpret_cast<DGNElemCore *>(psSpline);
979
2.26k
            psElement->stype = DGNST_BSPLINE_CURVE_HEADER;
980
2.26k
            DGNParseCore(psDGN, psElement);
981
982
            // Read B-Spline curve header
983
2.26k
            psSpline->desc_words =
984
2.26k
                static_cast<long>(DGN_INT32(psDGN->abyElem + 36));
985
986
            // flags
987
2.26k
            psSpline->order = (psDGN->abyElem[40] & 0x0f) + 2;
988
2.26k
            psSpline->properties = psDGN->abyElem[40] & 0xf0;
989
2.26k
            psSpline->curve_type = psDGN->abyElem[41];
990
991
2.26k
            psSpline->num_poles = psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
992
2.26k
            psSpline->num_knots = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
993
2.26k
        }
994
2.26k
        break;
995
19.8k
        case DGNT_BSPLINE_SURFACE_BOUNDARY:
996
19.8k
        {
997
19.8k
            short numverts = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
998
19.8k
            if (numverts <= 0)
999
17.0k
            {
1000
17.0k
                CPLError(CE_Failure, CPLE_AssertionFailed, "numverts <= 0");
1001
17.0k
                return nullptr;
1002
17.0k
            }
1003
1004
2.80k
            DGNElemBSplineSurfaceBoundary *psBounds =
1005
2.80k
                static_cast<DGNElemBSplineSurfaceBoundary *>(
1006
2.80k
                    CPLCalloc(sizeof(DGNElemBSplineSurfaceBoundary) +
1007
2.80k
                                  (numverts - 1) * sizeof(DGNPoint),
1008
2.80k
                              1));
1009
2.80k
            psElement = reinterpret_cast<DGNElemCore *>(psBounds);
1010
2.80k
            psElement->stype = DGNST_BSPLINE_SURFACE_BOUNDARY;
1011
2.80k
            DGNParseCore(psDGN, psElement);
1012
1013
2.80k
            int deltaLength = 0, deltaStart = 0;
1014
2.80k
            if (psBounds->core.properties & DGNPF_ATTRIBUTES)
1015
695
            {
1016
1.16M
                for (int iAttr = 0; iAttr < psBounds->core.attr_bytes - 3;
1017
1.16M
                     iAttr++)
1018
1.16M
                {
1019
1.16M
                    if (psBounds->core.attr_data[iAttr] == 0xA9 &&
1020
3.90k
                        psBounds->core.attr_data[iAttr + 1] == 0x51)
1021
246
                    {
1022
246
                        deltaLength =
1023
246
                            (psBounds->core.attr_data[iAttr + 2] +
1024
246
                             psBounds->core.attr_data[iAttr + 3] * 256) *
1025
246
                            2;
1026
246
                        deltaStart = iAttr + 6;
1027
246
                        break;
1028
246
                    }
1029
1.16M
                }
1030
695
            }
1031
            // Read B-Spline surface boundary
1032
2.80k
            psBounds->number = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1033
1034
1.26M
            for (int i = 0; i < numverts && 44 + i * 8 + 4 <= psDGN->nElemBytes;
1035
1.26M
                 i++)
1036
1.26M
            {
1037
1.26M
                psBounds->vertices[i].x =
1038
1.26M
                    DGN_INT32(psDGN->abyElem + 40 + i * 8);
1039
1.26M
                psBounds->vertices[i].y =
1040
1.26M
                    DGN_INT32(psDGN->abyElem + 44 + i * 8);
1041
1.26M
                psBounds->vertices[i].z = 0;
1042
1.26M
                if (deltaStart && deltaLength &&
1043
880k
                    deltaStart + i * 4 + 2 + 2 <= psBounds->core.attr_bytes)
1044
462k
                {
1045
462k
                    int dx = DGN_INT16(psBounds->core.attr_data + deltaStart +
1046
462k
                                       i * 4);
1047
462k
                    int dy = DGN_INT16(psBounds->core.attr_data + deltaStart +
1048
462k
                                       i * 4 + 2);
1049
462k
                    psBounds->vertices[i].x += dx / 32767.0;
1050
462k
                    psBounds->vertices[i].y += dy / 32767.0;
1051
462k
                }
1052
1.26M
                psBounds->numverts = static_cast<short>(i + 1);
1053
1.26M
            }
1054
2.80k
        }
1055
0
        break;
1056
430
        case DGNT_BSPLINE_KNOT:
1057
76.4k
        case DGNT_BSPLINE_WEIGHT_FACTOR:
1058
76.4k
        {
1059
            // FIXME: Is it OK to assume that the # of elements corresponds
1060
            // directly to the element size? kintel 20051215.
1061
76.4k
            int attr_bytes =
1062
76.4k
                psDGN->nElemBytes -
1063
76.4k
                (psDGN->abyElem[30] + psDGN->abyElem[31] * 256) * 2 - 32;
1064
76.4k
            if (attr_bytes < 0)
1065
75.6k
            {
1066
75.6k
                CPLError(CE_Failure, CPLE_AssertionFailed, "attr_bytes < 0");
1067
75.6k
                return nullptr;
1068
75.6k
            }
1069
786
            int numelems = (psDGN->nElemBytes - 36 - attr_bytes) / 4;
1070
786
            if (numelems < 1)
1071
753
            {
1072
753
                CPLError(CE_Failure, CPLE_AssertionFailed, "numelems < 1");
1073
753
                return nullptr;
1074
753
            }
1075
33
            DGNElemKnotWeight *psArray =
1076
33
                static_cast<DGNElemKnotWeight *>(CPLCalloc(
1077
33
                    sizeof(DGNElemKnotWeight) + (numelems - 1) * sizeof(float),
1078
33
                    1));
1079
1080
33
            psElement = reinterpret_cast<DGNElemCore *>(psArray);
1081
33
            psElement->stype = DGNST_KNOT_WEIGHT;
1082
33
            DGNParseCore(psDGN, psElement);
1083
1084
            // Read array
1085
59.9k
            for (int i = 0; i < numelems; i++)
1086
59.9k
            {
1087
59.9k
                psArray->array[i] = static_cast<float>(
1088
59.9k
                    1.0 * DGN_INT32(psDGN->abyElem + 36 + i * 4) /
1089
59.9k
                    ((1UL << 31) - 1));
1090
59.9k
            }
1091
33
        }
1092
0
        break;
1093
15.1k
        case DGNT_SHARED_CELL_DEFN:
1094
15.1k
        {
1095
15.1k
            DGNElemSharedCellDefn *psShared =
1096
15.1k
                static_cast<DGNElemSharedCellDefn *>(
1097
15.1k
                    CPLCalloc(sizeof(DGNElemSharedCellDefn), 1));
1098
15.1k
            psElement = reinterpret_cast<DGNElemCore *>(psShared);
1099
15.1k
            psElement->stype = DGNST_SHARED_CELL_DEFN;
1100
15.1k
            DGNParseCore(psDGN, psElement);
1101
1102
15.1k
            psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1103
15.1k
        }
1104
15.1k
        break;
1105
2.79M
        default:
1106
2.79M
        {
1107
2.79M
            psElement =
1108
2.79M
                static_cast<DGNElemCore *>(CPLCalloc(sizeof(DGNElemCore), 1));
1109
2.79M
            psElement->stype = DGNST_CORE;
1110
2.79M
            DGNParseCore(psDGN, psElement);
1111
2.79M
        }
1112
2.79M
        break;
1113
4.40M
    }
1114
1115
    /* -------------------------------------------------------------------- */
1116
    /*      If the element structure type is "core" or if we are running    */
1117
    /*      in "capture all" mode, record the complete binary image of      */
1118
    /*      the element.                                                    */
1119
    /* -------------------------------------------------------------------- */
1120
4.22M
    if (psElement->stype == DGNST_CORE ||
1121
1.41M
        (psDGN->options & DGNO_CAPTURE_RAW_DATA))
1122
2.80M
    {
1123
2.80M
        psElement->raw_bytes = psDGN->nElemBytes;
1124
2.80M
        psElement->raw_data =
1125
2.80M
            static_cast<unsigned char *>(CPLMalloc(psElement->raw_bytes));
1126
1127
2.80M
        memcpy(psElement->raw_data, psDGN->abyElem, psElement->raw_bytes);
1128
2.80M
    }
1129
1130
    /* -------------------------------------------------------------------- */
1131
    /*      Collect some additional generic information.                    */
1132
    /* -------------------------------------------------------------------- */
1133
4.22M
    psElement->element_id = psDGN->next_element_id - 1;
1134
1135
4.22M
    psElement->offset =
1136
4.22M
        static_cast<int>(VSIFTellL(psDGN->fp)) - psDGN->nElemBytes;
1137
4.22M
    psElement->size = psDGN->nElemBytes;
1138
1139
4.22M
    return psElement;
1140
4.40M
}
1141
1142
/************************************************************************/
1143
/*                           DGNReadElement()                           */
1144
/************************************************************************/
1145
1146
/**
1147
 * Read a DGN element.
1148
 *
1149
 * This function will return the next element in the file, starting with the
1150
 * first.  It is affected by DGNGotoElement() calls.
1151
 *
1152
 * The element is read into a structure which includes the DGNElemCore
1153
 * structure.  It is expected that applications will inspect the stype
1154
 * field of the returned DGNElemCore and use it to cast the pointer to the
1155
 * appropriate element structure type such as DGNElemMultiPoint.
1156
 *
1157
 * @param hDGN the handle of the file to read from.
1158
 *
1159
 * @return pointer to element structure, or NULL on EOF or processing error.
1160
 * The structure should be freed with DGNFreeElement() when no longer needed.
1161
 */
1162
1163
DGNElemCore *DGNReadElement(DGNHandle hDGN)
1164
1165
296M
{
1166
296M
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1167
296M
    int nType = 0;
1168
296M
    int nLevel = 0;
1169
296M
    bool bInsideFilter = false;
1170
1171
    /* -------------------------------------------------------------------- */
1172
    /*      Load the element data into the current buffer.  If a spatial    */
1173
    /*      filter is in effect, loop until we get something within our     */
1174
    /*      spatial constraints.                                            */
1175
    /* -------------------------------------------------------------------- */
1176
296M
    do
1177
296M
    {
1178
296M
        bInsideFilter = true;
1179
1180
296M
        if (!DGNLoadRawElement(psDGN, &nType, &nLevel))
1181
291M
            return nullptr;
1182
1183
4.40M
        if (psDGN->has_spatial_filter)
1184
0
        {
1185
0
            if (!psDGN->sf_converted_to_uor)
1186
0
                DGNSpatialFilterToUOR(psDGN);
1187
1188
0
            GUInt32 nXMin = 0;
1189
0
            GUInt32 nXMax = 0;
1190
0
            GUInt32 nYMin = 0;
1191
0
            GUInt32 nYMax = 0;
1192
0
            if (!DGNGetRawExtents(psDGN, nType, nullptr, &nXMin, &nYMin,
1193
0
                                  nullptr, &nXMax, &nYMax, nullptr))
1194
0
            {
1195
                /* If we don't have spatial characteristics for the element
1196
                   we will pass it through. */
1197
0
                bInsideFilter = true;
1198
0
            }
1199
0
            else if (nXMin > psDGN->sf_max_x || nYMin > psDGN->sf_max_y ||
1200
0
                     nXMax < psDGN->sf_min_x || nYMax < psDGN->sf_min_y)
1201
0
            {
1202
0
                bInsideFilter = false;
1203
0
            }
1204
1205
            /*
1206
            ** We want to select complex elements based on the extents of
1207
            ** the header, not the individual elements.
1208
            */
1209
0
            if (nType == DGNT_COMPLEX_CHAIN_HEADER ||
1210
0
                nType == DGNT_COMPLEX_SHAPE_HEADER)
1211
0
            {
1212
0
                psDGN->in_complex_group = true;
1213
0
                psDGN->select_complex_group = bInsideFilter;
1214
0
            }
1215
0
            else if (psDGN->abyElem[0] & 0x80 /* complex flag set */)
1216
0
            {
1217
0
                if (psDGN->in_complex_group)
1218
0
                    bInsideFilter = psDGN->select_complex_group;
1219
0
            }
1220
0
            else
1221
0
            {
1222
0
                psDGN->in_complex_group = false;
1223
0
            }
1224
0
        }
1225
4.40M
    } while (!bInsideFilter);
1226
1227
    /* -------------------------------------------------------------------- */
1228
    /*      Convert into an element structure.                              */
1229
    /* -------------------------------------------------------------------- */
1230
4.40M
    DGNElemCore *psElement = DGNProcessElement(psDGN, nType, nLevel);
1231
1232
4.40M
    return psElement;
1233
296M
}
1234
1235
/************************************************************************/
1236
/*                       DGNElemTypeHasDispHdr()                        */
1237
/************************************************************************/
1238
1239
/**
1240
 * Does element type have display header.
1241
 *
1242
 * @param nElemType element type (0-63) to test.
1243
 *
1244
 * @return TRUE if elements of passed in type have a display header after the
1245
 * core element header, or FALSE otherwise.
1246
 */
1247
1248
int DGNElemTypeHasDispHdr(int nElemType)
1249
1250
113k
{
1251
113k
    switch (nElemType)
1252
113k
    {
1253
58.6k
        case 0:
1254
59.3k
        case DGNT_TCB:
1255
62.3k
        case DGNT_CELL_LIBRARY:
1256
62.6k
        case DGNT_LEVEL_SYMBOLOGY:
1257
62.7k
        case 32:
1258
62.7k
        case 44:
1259
62.8k
        case 48:
1260
62.8k
        case 49:
1261
62.9k
        case 50:
1262
63.0k
        case 51:
1263
63.0k
        case 57:
1264
63.0k
        case 60:
1265
64.2k
        case 61:
1266
64.2k
        case 62:
1267
64.2k
        case 63:
1268
64.2k
            return FALSE;
1269
1270
49.7k
        default:
1271
49.7k
            return TRUE;
1272
113k
    }
1273
113k
}
1274
1275
/************************************************************************/
1276
/*                            DGNParseCore()                            */
1277
/************************************************************************/
1278
1279
int DGNParseCore(DGNInfo *psDGN, DGNElemCore *psElement)
1280
1281
4.30M
{
1282
4.30M
    GByte *psData = psDGN->abyElem + 0;
1283
1284
4.30M
    psElement->level = psData[0] & 0x3f;
1285
4.30M
    psElement->complex = psData[0] & 0x80;
1286
4.30M
    psElement->deleted = psData[1] & 0x80;
1287
4.30M
    psElement->type = psData[1] & 0x7f;
1288
1289
4.30M
    if (psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr(psElement->type))
1290
49.7k
    {
1291
49.7k
        psElement->graphic_group = psData[28] + psData[29] * 256;
1292
49.7k
        psElement->properties = psData[32] + psData[33] * 256;
1293
49.7k
        psElement->style = psData[34] & 0x7;
1294
49.7k
        psElement->weight = (psData[34] & 0xf8) >> 3;
1295
49.7k
        psElement->color = psData[35];
1296
49.7k
    }
1297
4.25M
    else
1298
4.25M
    {
1299
4.25M
        psElement->graphic_group = 0;
1300
4.25M
        psElement->properties = 0;
1301
4.25M
        psElement->style = 0;
1302
4.25M
        psElement->weight = 0;
1303
4.25M
        psElement->color = 0;
1304
4.25M
    }
1305
1306
4.30M
    if (psElement->properties & DGNPF_ATTRIBUTES)
1307
19.9k
    {
1308
19.9k
        const int nAttIndex = psData[30] + psData[31] * 256;
1309
1310
19.9k
        psElement->attr_bytes = psDGN->nElemBytes - nAttIndex * 2 - 32;
1311
19.9k
        if (psElement->attr_bytes > 0)
1312
7.30k
        {
1313
7.30k
            psElement->attr_data =
1314
7.30k
                static_cast<unsigned char *>(CPLMalloc(psElement->attr_bytes));
1315
7.30k
            memcpy(psElement->attr_data, psData + nAttIndex * 2 + 32,
1316
7.30k
                   psElement->attr_bytes);
1317
7.30k
        }
1318
12.6k
        else
1319
12.6k
        {
1320
12.6k
            CPLError(CE_Warning, CPLE_AppDefined,
1321
12.6k
                     "Computed %d bytes for attribute info on element,\n"
1322
12.6k
                     "perhaps this element type doesn't really have a disphdr?",
1323
12.6k
                     psElement->attr_bytes);
1324
12.6k
            psElement->attr_bytes = 0;
1325
12.6k
        }
1326
19.9k
    }
1327
1328
4.30M
    return TRUE;
1329
4.30M
}
1330
1331
/************************************************************************/
1332
/*                         DGNParseColorTable()                         */
1333
/************************************************************************/
1334
1335
static DGNElemCore *DGNParseColorTable(DGNInfo *psDGN)
1336
1337
15.7k
{
1338
15.7k
    DGNElemColorTable *psColorTable = static_cast<DGNElemColorTable *>(
1339
15.7k
        CPLCalloc(sizeof(DGNElemColorTable), 1));
1340
15.7k
    DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psColorTable);
1341
15.7k
    psElement->stype = DGNST_COLORTABLE;
1342
1343
15.7k
    DGNParseCore(psDGN, psElement);
1344
1345
15.7k
    psColorTable->screen_flag = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1346
1347
15.7k
    memcpy(psColorTable->color_info[255], psDGN->abyElem + 38, 3);
1348
15.7k
    memcpy(psColorTable->color_info, psDGN->abyElem + 41, 765);
1349
1350
    // We used to only install a color table as the default color
1351
    // table if it was the first in the file.  But apparently we should
1352
    // really be using the last one.  This doesn't necessarily accomplish
1353
    // that either if the elements are being read out of order but it will
1354
    // usually do better at least.
1355
15.7k
    memcpy(psDGN->color_table, psColorTable->color_info, 768);
1356
15.7k
    psDGN->got_color_table = 1;
1357
1358
15.7k
    return psElement;
1359
15.7k
}
1360
1361
/************************************************************************/
1362
/*                           DGNParseTagSet()                           */
1363
/************************************************************************/
1364
1365
static DGNElemCore *DGNParseTagSet(DGNInfo *psDGN)
1366
1367
1.68k
{
1368
1.68k
    DGNElemTagSet *psTagSet =
1369
1.68k
        static_cast<DGNElemTagSet *>(CPLCalloc(sizeof(DGNElemTagSet), 1));
1370
1.68k
    DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTagSet);
1371
1.68k
    psElement->stype = DGNST_TAG_SET;
1372
1373
1.68k
    DGNParseCore(psDGN, psElement);
1374
1375
    /* -------------------------------------------------------------------- */
1376
    /*      Parse the overall information.                                  */
1377
    /* -------------------------------------------------------------------- */
1378
1.68k
    psTagSet->tagCount = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
1379
1.68k
    psTagSet->flags = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
1380
1.68k
    psTagSet->tagSetName = CPLStrdup((const char *)(psDGN->abyElem + 48));
1381
1382
    /* -------------------------------------------------------------------- */
1383
    /*      Get the tag set number out of the attributes, if available.     */
1384
    /* -------------------------------------------------------------------- */
1385
1.68k
    psTagSet->tagSet = -1;
1386
1387
1.68k
    if (psElement->attr_bytes >= 8 && psElement->attr_data[0] == 0x03 &&
1388
0
        psElement->attr_data[1] == 0x10 && psElement->attr_data[2] == 0x2f &&
1389
0
        psElement->attr_data[3] == 0x7d)
1390
0
        psTagSet->tagSet =
1391
0
            psElement->attr_data[4] + psElement->attr_data[5] * 256;
1392
1393
    /* -------------------------------------------------------------------- */
1394
    /*      Parse each of the tag definitions.                              */
1395
    /* -------------------------------------------------------------------- */
1396
1.68k
    psTagSet->tagList = static_cast<DGNTagDef *>(
1397
1.68k
        CPLCalloc(sizeof(DGNTagDef), psTagSet->tagCount));
1398
1399
1.68k
    size_t nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1;
1400
1401
109k
    for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
1402
108k
    {
1403
108k
        DGNTagDef *tagDef = psTagSet->tagList + iTag;
1404
1405
        // Check the buffer is large enough to read all tagDef components
1406
108k
        size_t nDataOffsetEnd = nDataOffset;
1407
108k
        if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
1408
107k
        {
1409
107k
            nDataOffsetEnd +=
1410
107k
                strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2;
1411
107k
        }
1412
108k
        if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
1413
107k
        {
1414
107k
            nDataOffsetEnd +=
1415
107k
                strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2 + 5;
1416
107k
            if (tagDef->type == 1)
1417
0
            {
1418
0
                nDataOffsetEnd += strlen(tagDef->defaultValue.string) + 1;
1419
0
            }
1420
107k
            else if (tagDef->type == 3 || tagDef->type == 5)
1421
0
            {
1422
0
                nDataOffsetEnd += 4;
1423
0
            }
1424
107k
            else if (tagDef->type == 4)
1425
0
            {
1426
0
                nDataOffsetEnd += 8;
1427
0
            }
1428
107k
            else
1429
107k
            {
1430
107k
                nDataOffsetEnd += 4;
1431
107k
            }
1432
107k
        }
1433
108k
        if (nDataOffsetEnd > static_cast<size_t>(psDGN->nElemBytes))
1434
728
        {
1435
728
            CPLError(CE_Failure, CPLE_AssertionFailed,
1436
728
                     "nDataOffset >= static_cast<size_t>(psDGN->nElemBytes)");
1437
728
            DGNFreeElement(psDGN, psElement);
1438
728
            return nullptr;
1439
728
        }
1440
1441
        /* collect tag name. */
1442
107k
        tagDef->name = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1443
107k
        nDataOffset += strlen(tagDef->name) + 1;
1444
1445
        /* Get tag id */
1446
107k
        tagDef->id =
1447
107k
            psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
1448
107k
        nDataOffset += 2;
1449
1450
        /* Get User Prompt */
1451
107k
        tagDef->prompt = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1452
107k
        nDataOffset += strlen(tagDef->prompt) + 1;
1453
1454
        /* Get type */
1455
107k
        tagDef->type =
1456
107k
            psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
1457
107k
        nDataOffset += 2;
1458
1459
        /* skip five zeros */
1460
107k
        nDataOffset += 5;
1461
1462
        /* Get the default */
1463
107k
        if (tagDef->type == 1)
1464
180
        {
1465
180
            tagDef->defaultValue.string =
1466
180
                CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1467
180
            nDataOffset += strlen(tagDef->defaultValue.string) + 1;
1468
180
        }
1469
107k
        else if (tagDef->type == 3 || tagDef->type == 5)
1470
652
        {
1471
652
            memcpy(&(tagDef->defaultValue.integer),
1472
652
                   psDGN->abyElem + nDataOffset, 4);
1473
652
            CPL_LSBPTR32(&(tagDef->defaultValue.integer));
1474
652
            nDataOffset += 4;
1475
652
        }
1476
106k
        else if (tagDef->type == 4)
1477
113
        {
1478
113
            memcpy(&(tagDef->defaultValue.real), psDGN->abyElem + nDataOffset,
1479
113
                   8);
1480
113
            DGN2IEEEDouble(&(tagDef->defaultValue.real));
1481
113
            nDataOffset += 8;
1482
113
        }
1483
106k
        else
1484
106k
            nDataOffset += 4;
1485
107k
    }
1486
959
    return psElement;
1487
1.68k
}
1488
1489
/************************************************************************/
1490
/*                            DGNParseTCB()                             */
1491
/************************************************************************/
1492
1493
static DGNElemCore *DGNParseTCB(DGNInfo *psDGN)
1494
1495
97.1k
{
1496
97.1k
    DGNElemTCB *psTCB =
1497
97.1k
        static_cast<DGNElemTCB *>(CPLCalloc(sizeof(DGNElemTCB), 1));
1498
97.1k
    DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTCB);
1499
97.1k
    psElement->stype = DGNST_TCB;
1500
97.1k
    DGNParseCore(psDGN, psElement);
1501
1502
97.1k
    if (psDGN->abyElem[1214] & 0x40)
1503
6.31k
        psTCB->dimension = 3;
1504
90.8k
    else
1505
90.8k
        psTCB->dimension = 2;
1506
1507
97.1k
    psTCB->subunits_per_master =
1508
97.1k
        static_cast<long>(DGN_INT32(psDGN->abyElem + 1112));
1509
1510
97.1k
    psTCB->master_units[0] = (char)psDGN->abyElem[1120];
1511
97.1k
    psTCB->master_units[1] = (char)psDGN->abyElem[1121];
1512
97.1k
    psTCB->master_units[2] = '\0';
1513
1514
97.1k
    psTCB->uor_per_subunit =
1515
97.1k
        static_cast<long>(DGN_INT32(psDGN->abyElem + 1116));
1516
1517
97.1k
    psTCB->sub_units[0] = (char)psDGN->abyElem[1122];
1518
97.1k
    psTCB->sub_units[1] = (char)psDGN->abyElem[1123];
1519
97.1k
    psTCB->sub_units[2] = '\0';
1520
1521
    /* Get global origin */
1522
97.1k
    memcpy(&(psTCB->origin_x), psDGN->abyElem + 1240, 8);
1523
97.1k
    memcpy(&(psTCB->origin_y), psDGN->abyElem + 1248, 8);
1524
97.1k
    memcpy(&(psTCB->origin_z), psDGN->abyElem + 1256, 8);
1525
1526
    /* Transform to IEEE */
1527
97.1k
    DGN2IEEEDouble(&(psTCB->origin_x));
1528
97.1k
    DGN2IEEEDouble(&(psTCB->origin_y));
1529
97.1k
    DGN2IEEEDouble(&(psTCB->origin_z));
1530
1531
    /* Convert from UORs to master units. */
1532
97.1k
    if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
1533
49.7k
    {
1534
49.7k
        psTCB->origin_x = psTCB->origin_x /
1535
49.7k
                          (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1536
49.7k
        psTCB->origin_y = psTCB->origin_y /
1537
49.7k
                          (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1538
49.7k
        psTCB->origin_z = psTCB->origin_z /
1539
49.7k
                          (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1540
49.7k
    }
1541
1542
97.1k
    if (!psDGN->got_tcb)
1543
2.23k
    {
1544
2.23k
        psDGN->got_tcb = true;
1545
2.23k
        psDGN->dimension = psTCB->dimension;
1546
2.23k
        psDGN->origin_x = psTCB->origin_x;
1547
2.23k
        psDGN->origin_y = psTCB->origin_y;
1548
2.23k
        psDGN->origin_z = psTCB->origin_z;
1549
1550
2.23k
        if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
1551
666
            psDGN->scale =
1552
666
                1.0 / (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1553
2.23k
    }
1554
1555
    /* Collect views */
1556
874k
    for (int iView = 0; iView < 8; iView++)
1557
777k
    {
1558
777k
        unsigned char *pabyRawView = psDGN->abyElem + 46 + iView * 118;
1559
777k
        DGNViewInfo *psView = psTCB->views + iView;
1560
1561
777k
        psView->flags = pabyRawView[0] + pabyRawView[1] * 256;
1562
777k
        memcpy(psView->levels, pabyRawView + 2, 8);
1563
1564
777k
        psView->origin.x = DGN_INT32(pabyRawView + 10);
1565
777k
        psView->origin.y = DGN_INT32(pabyRawView + 14);
1566
777k
        psView->origin.z = DGN_INT32(pabyRawView + 18);
1567
1568
777k
        DGNTransformPoint(psDGN, &(psView->origin));
1569
1570
777k
        psView->delta.x = DGN_INT32(pabyRawView + 22);
1571
777k
        psView->delta.y = DGN_INT32(pabyRawView + 26);
1572
777k
        psView->delta.z = DGN_INT32(pabyRawView + 30);
1573
1574
777k
        psView->delta.x *= psDGN->scale;
1575
777k
        psView->delta.y *= psDGN->scale;
1576
777k
        psView->delta.z *= psDGN->scale;
1577
1578
777k
        memcpy(psView->transmatrx, pabyRawView + 34, sizeof(double) * 9);
1579
7.77M
        for (int i = 0; i < 9; i++)
1580
6.99M
            DGN2IEEEDouble(psView->transmatrx + i);
1581
1582
777k
        memcpy(&(psView->conversion), pabyRawView + 106, sizeof(double));
1583
777k
        DGN2IEEEDouble(&(psView->conversion));
1584
1585
777k
        psView->activez =
1586
777k
            static_cast<unsigned long>(DGN_INT32(pabyRawView + 114));
1587
777k
    }
1588
1589
97.1k
    return psElement;
1590
97.1k
}
1591
1592
/************************************************************************/
1593
/*                           DGNFreeElement()                           */
1594
/************************************************************************/
1595
1596
/**
1597
 * Free an element structure.
1598
 *
1599
 * This function will deallocate all resources associated with any element
1600
 * structure returned by DGNReadElement().
1601
 *
1602
 * @param hDGN handle to file from which the element was read.
1603
 * @param psElement the element structure returned by DGNReadElement().
1604
 */
1605
1606
void DGNFreeElement(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement)
1607
4.30M
{
1608
4.30M
    if (psElement->attr_data != nullptr)
1609
7.30k
        VSIFree(psElement->attr_data);
1610
1611
4.30M
    if (psElement->raw_data != nullptr)
1612
2.80M
        VSIFree(psElement->raw_data);
1613
1614
4.30M
    if (psElement->stype == DGNST_TAG_SET)
1615
1.68k
    {
1616
1.68k
        DGNElemTagSet *psTagSet = reinterpret_cast<DGNElemTagSet *>(psElement);
1617
1.68k
        CPLFree(psTagSet->tagSetName);
1618
1619
6.98M
        for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
1620
6.97M
        {
1621
6.97M
            CPLFree(psTagSet->tagList[iTag].name);
1622
6.97M
            CPLFree(psTagSet->tagList[iTag].prompt);
1623
1624
6.97M
            if (psTagSet->tagList[iTag].type == 1)
1625
180
                CPLFree(psTagSet->tagList[iTag].defaultValue.string);
1626
6.97M
        }
1627
1.68k
        CPLFree(psTagSet->tagList);
1628
1.68k
    }
1629
4.30M
    else if (psElement->stype == DGNST_TAG_VALUE)
1630
12.2k
    {
1631
12.2k
        DGNElemTagValue *psTagValue =
1632
12.2k
            reinterpret_cast<DGNElemTagValue *>(psElement);
1633
12.2k
        if (psTagValue->tagType == 1)
1634
520
            CPLFree(psTagValue->tagValue.string);
1635
12.2k
    }
1636
1637
4.30M
    CPLFree(psElement);
1638
4.30M
}
1639
1640
/************************************************************************/
1641
/*                             DGNRewind()                              */
1642
/************************************************************************/
1643
1644
/**
1645
 * Rewind element reading.
1646
 *
1647
 * Rewind the indicated DGN file, so the next element read with
1648
 * DGNReadElement() will be the first.  Does not require indexing like
1649
 * the more general DGNReadElement() function.
1650
 *
1651
 * @param hDGN handle to file.
1652
 */
1653
1654
void DGNRewind(DGNHandle hDGN)
1655
1656
8.34k
{
1657
8.34k
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1658
1659
8.34k
    VSIRewindL(psDGN->fp);
1660
1661
8.34k
    psDGN->next_element_id = 0;
1662
8.34k
    psDGN->in_complex_group = false;
1663
8.34k
}
1664
1665
/************************************************************************/
1666
/*                         DGNTransformPoint()                          */
1667
/************************************************************************/
1668
1669
void DGNTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
1670
1671
9.66M
{
1672
9.66M
    psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x;
1673
9.66M
    psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y;
1674
9.66M
    psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z;
1675
9.66M
}
1676
1677
/************************************************************************/
1678
/*                      DGNInverseTransformPoint()                      */
1679
/************************************************************************/
1680
1681
void DGNInverseTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
1682
1683
0
{
1684
0
    psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale;
1685
0
    psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale;
1686
0
    psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale;
1687
1688
0
    psPoint->x = std::max(-2147483647.0, std::min(2147483647.0, psPoint->x));
1689
0
    psPoint->y = std::max(-2147483647.0, std::min(2147483647.0, psPoint->y));
1690
0
    psPoint->z = std::max(-2147483647.0, std::min(2147483647.0, psPoint->z));
1691
0
}
1692
1693
/************************************************************************/
1694
/*                   DGNInverseTransformPointToInt()                    */
1695
/************************************************************************/
1696
1697
void DGNInverseTransformPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
1698
                                   unsigned char *pabyTarget)
1699
1700
0
{
1701
0
    double adfCT[3] = {(psPoint->x + psDGN->origin_x) / psDGN->scale,
1702
0
                       (psPoint->y + psDGN->origin_y) / psDGN->scale,
1703
0
                       (psPoint->z + psDGN->origin_z) / psDGN->scale};
1704
1705
0
    const int nIter = std::min(3, psDGN->dimension);
1706
0
    for (int i = 0; i < nIter; i++)
1707
0
    {
1708
0
        GInt32 nCTI = static_cast<GInt32>(
1709
0
            std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
1710
0
        unsigned char abyCTI[4];
1711
0
        memcpy(abyCTI, &nCTI, sizeof(GInt32));
1712
1713
#ifdef WORDS_BIGENDIAN
1714
        pabyTarget[i * 4 + 0] = abyCTI[1];
1715
        pabyTarget[i * 4 + 1] = abyCTI[0];
1716
        pabyTarget[i * 4 + 2] = abyCTI[3];
1717
        pabyTarget[i * 4 + 3] = abyCTI[2];
1718
#else
1719
0
        pabyTarget[i * 4 + 3] = abyCTI[1];
1720
0
        pabyTarget[i * 4 + 2] = abyCTI[0];
1721
0
        pabyTarget[i * 4 + 1] = abyCTI[3];
1722
0
        pabyTarget[i * 4 + 0] = abyCTI[2];
1723
0
#endif
1724
0
    }
1725
0
}
1726
1727
/************************************************************************/
1728
/*                             DGNLoadTCB()                             */
1729
/************************************************************************/
1730
1731
/**
1732
 * Load TCB if not already loaded.
1733
 *
1734
 * This function will load the TCB element if it is not already loaded.
1735
 * It is used primarily to ensure the TCB is loaded before doing any operations
1736
 * that require TCB values (like creating new elements).
1737
 *
1738
 * @return FALSE on failure or TRUE on success.
1739
 */
1740
1741
int DGNLoadTCB(DGNHandle hDGN)
1742
1743
33
{
1744
33
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1745
1746
33
    if (psDGN->got_tcb)
1747
30
        return TRUE;
1748
1749
6
    while (!psDGN->got_tcb)
1750
3
    {
1751
3
        DGNElemCore *psElem = DGNReadElement(hDGN);
1752
3
        if (psElem == nullptr)
1753
0
        {
1754
0
            CPLError(CE_Failure, CPLE_AppDefined,
1755
0
                     "DGNLoadTCB() - unable to find TCB in file.");
1756
0
            return FALSE;
1757
0
        }
1758
3
        DGNFreeElement(hDGN, psElem);
1759
3
    }
1760
1761
3
    return TRUE;
1762
3
}
1763
1764
/************************************************************************/
1765
/*                         DGNGetElementIndex()                         */
1766
/************************************************************************/
1767
1768
/**
1769
 * Fetch element index.
1770
 *
1771
 * This function will return an array with brief information about every
1772
 * element in a DGN file.  It requires one pass through the entire file to
1773
 * generate (this is not repeated on subsequent calls).
1774
 *
1775
 * The returned array of DGNElementInfo structures contain the level, type,
1776
 * stype, and other flags for each element in the file.  This can facilitate
1777
 * application level code representing the number of elements of various types
1778
 * efficiently.
1779
 *
1780
 * Note that while building the index requires one pass through the whole file,
1781
 * it does not generally request much processing for each element.
1782
 *
1783
 * @param hDGN the file to get an index for.
1784
 * @param pnElementCount the integer to put the total element count into.
1785
 *
1786
 * @return a pointer to an internal array of DGNElementInfo structures (there
1787
 * will be *pnElementCount entries in the array), or NULL on failure.  The
1788
 * returned array should not be modified or freed, and will last only as long
1789
 * as the DGN file remains open.
1790
 */
1791
1792
const DGNElementInfo *DGNGetElementIndex(DGNHandle hDGN, int *pnElementCount)
1793
1794
20.4k
{
1795
20.4k
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1796
1797
20.4k
    DGNBuildIndex(psDGN);
1798
1799
20.4k
    if (pnElementCount != nullptr)
1800
0
        *pnElementCount = psDGN->element_count;
1801
1802
20.4k
    return psDGN->element_index;
1803
20.4k
}
1804
1805
/************************************************************************/
1806
/*                           DGNGetExtents()                            */
1807
/************************************************************************/
1808
1809
/**
1810
 * Fetch overall file extents.
1811
 *
1812
 * The extents are collected for each element while building an index, so
1813
 * if an index has not already been built, it will be built when
1814
 * DGNGetExtents() is called.
1815
 *
1816
 * The Z min/max values are generally meaningless (0 and 0xffffffff in uor
1817
 * space).
1818
 *
1819
 * @param hDGN the file to get extents for.
1820
 * @param padfExtents pointer to an array of six doubles into which are loaded
1821
 * the values xmin, ymin, zmin, xmax, ymax, and zmax.
1822
 *
1823
 * @return TRUE on success or FALSE on failure.
1824
 */
1825
1826
int DGNGetExtents(DGNHandle hDGN, double *padfExtents)
1827
1828
0
{
1829
0
    DGNInfo *psDGN = (DGNInfo *)hDGN;
1830
1831
0
    DGNBuildIndex(psDGN);
1832
1833
0
    if (!psDGN->got_bounds)
1834
0
        return FALSE;
1835
1836
0
    DGNPoint sMin = {psDGN->min_x - 2147483648.0, psDGN->min_y - 2147483648.0,
1837
0
                     psDGN->min_z - 2147483648.0};
1838
1839
0
    DGNTransformPoint(psDGN, &sMin);
1840
1841
0
    padfExtents[0] = sMin.x;
1842
0
    padfExtents[1] = sMin.y;
1843
0
    padfExtents[2] = sMin.z;
1844
1845
0
    DGNPoint sMax = {psDGN->max_x - 2147483648.0, psDGN->max_y - 2147483648.0,
1846
0
                     psDGN->max_z - 2147483648.0};
1847
1848
0
    DGNTransformPoint(psDGN, &sMax);
1849
1850
0
    padfExtents[3] = sMax.x;
1851
0
    padfExtents[4] = sMax.y;
1852
0
    padfExtents[5] = sMax.z;
1853
1854
0
    return TRUE;
1855
0
}
1856
1857
/************************************************************************/
1858
/*                           DGNBuildIndex()                            */
1859
/************************************************************************/
1860
1861
void DGNBuildIndex(DGNInfo *psDGN)
1862
1863
20.4k
{
1864
20.4k
    if (psDGN->index_built)
1865
16.3k
        return;
1866
1867
4.17k
    int nType = 0;
1868
4.17k
    int nLevel = 0;
1869
4.17k
    GUInt32 anRegion[6] = {};
1870
1871
4.17k
    psDGN->index_built = true;
1872
1873
4.17k
    DGNRewind(psDGN);
1874
1875
4.17k
    int nMaxElements = 0;
1876
1877
4.17k
    vsi_l_offset nLastOffset = VSIFTellL(psDGN->fp);
1878
4.07M
    while (DGNLoadRawElement(psDGN, &nType, &nLevel))
1879
4.06M
    {
1880
4.06M
        if (psDGN->element_count == nMaxElements)
1881
6.71k
        {
1882
6.71k
            nMaxElements = (int)(nMaxElements * 1.5) + 500;
1883
1884
6.71k
            psDGN->element_index = (DGNElementInfo *)CPLRealloc(
1885
6.71k
                psDGN->element_index, nMaxElements * sizeof(DGNElementInfo));
1886
6.71k
        }
1887
1888
4.06M
        DGNElementInfo *psEI = psDGN->element_index + psDGN->element_count;
1889
4.06M
        psEI->level = (unsigned char)nLevel;
1890
4.06M
        psEI->type = (unsigned char)nType;
1891
4.06M
        psEI->flags = 0;
1892
4.06M
        psEI->offset = nLastOffset;
1893
1894
4.06M
        if (psDGN->abyElem[0] & 0x80)
1895
32.0k
            psEI->flags |= DGNEIF_COMPLEX;
1896
1897
4.06M
        if (psDGN->abyElem[1] & 0x80)
1898
84.1k
            psEI->flags |= DGNEIF_DELETED;
1899
1900
4.06M
        if (nType == DGNT_LINE || nType == DGNT_LINE_STRING ||
1901
3.79M
            nType == DGNT_SHAPE || nType == DGNT_CURVE ||
1902
3.77M
            nType == DGNT_BSPLINE_POLE)
1903
305k
            psEI->stype = DGNST_MULTIPOINT;
1904
1905
3.76M
        else if (nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE)
1906
6.93k
        {
1907
6.93k
            DGNElemCore *psCT = DGNParseColorTable(psDGN);
1908
6.93k
            DGNFreeElement((DGNHandle)psDGN, psCT);
1909
6.93k
            psEI->stype = DGNST_COLORTABLE;
1910
6.93k
        }
1911
3.75M
        else if (nType == DGNT_ELLIPSE || nType == DGNT_ARC)
1912
231k
            psEI->stype = DGNST_ARC;
1913
1914
3.52M
        else if (nType == DGNT_COMPLEX_SHAPE_HEADER ||
1915
3.36M
                 nType == DGNT_COMPLEX_CHAIN_HEADER ||
1916
3.23M
                 nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER)
1917
343k
            psEI->stype = DGNST_COMPLEX_HEADER;
1918
1919
3.17M
        else if (nType == DGNT_TEXT)
1920
53.5k
            psEI->stype = DGNST_TEXT;
1921
1922
3.12M
        else if (nType == DGNT_TAG_VALUE)
1923
12.0k
            psEI->stype = DGNST_TAG_VALUE;
1924
1925
3.11M
        else if (nType == DGNT_APPLICATION_ELEM)
1926
8.66k
        {
1927
8.66k
            if (nLevel == 24)
1928
1.35k
                psEI->stype = DGNST_TAG_SET;
1929
7.31k
            else
1930
7.31k
                psEI->stype = DGNST_CORE;
1931
8.66k
        }
1932
3.10M
        else if (nType == DGNT_TCB)
1933
44.2k
        {
1934
44.2k
            DGNElemCore *psTCB = DGNParseTCB(psDGN);
1935
44.2k
            DGNFreeElement((DGNHandle)psDGN, psTCB);
1936
44.2k
            psEI->stype = DGNST_TCB;
1937
44.2k
        }
1938
3.06M
        else if (nType == DGNT_CONE)
1939
27.0k
            psEI->stype = DGNST_CONE;
1940
3.03M
        else
1941
3.03M
            psEI->stype = DGNST_CORE;
1942
1943
4.06M
        if (!(psEI->flags & DGNEIF_DELETED) &&
1944
3.98M
            !(psEI->flags & DGNEIF_COMPLEX) &&
1945
3.95M
            DGNGetRawExtents(psDGN, nType, nullptr, anRegion + 0, anRegion + 1,
1946
3.95M
                             anRegion + 2, anRegion + 3, anRegion + 4,
1947
3.95M
                             anRegion + 5))
1948
945k
        {
1949
#ifdef notdef
1950
            printf("panRegion[%d]=%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n", /*ok*/
1951
                   psDGN->element_count, anRegion[0] - 2147483648.0,
1952
                   anRegion[1] - 2147483648.0, anRegion[2] - 2147483648.0,
1953
                   anRegion[3] - 2147483648.0, anRegion[4] - 2147483648.0,
1954
                   anRegion[5] - 2147483648.0);
1955
#endif
1956
945k
            if (psDGN->got_bounds)
1957
942k
            {
1958
942k
                psDGN->min_x = std::min(psDGN->min_x, anRegion[0]);
1959
942k
                psDGN->min_y = std::min(psDGN->min_y, anRegion[1]);
1960
942k
                psDGN->min_z = std::min(psDGN->min_z, anRegion[2]);
1961
942k
                psDGN->max_x = std::max(psDGN->max_x, anRegion[3]);
1962
942k
                psDGN->max_y = std::max(psDGN->max_y, anRegion[4]);
1963
942k
                psDGN->max_z = std::max(psDGN->max_z, anRegion[5]);
1964
942k
            }
1965
3.85k
            else
1966
3.85k
            {
1967
3.85k
                psDGN->min_x = anRegion[0];
1968
3.85k
                psDGN->min_y = anRegion[1];
1969
3.85k
                psDGN->min_z = anRegion[2];
1970
3.85k
                psDGN->max_x = anRegion[3];
1971
3.85k
                psDGN->max_y = anRegion[4];
1972
3.85k
                psDGN->max_z = anRegion[5];
1973
3.85k
                psDGN->got_bounds = true;
1974
3.85k
            }
1975
945k
        }
1976
1977
4.06M
        psDGN->element_count++;
1978
1979
4.06M
        nLastOffset = VSIFTellL(psDGN->fp);
1980
4.06M
    }
1981
1982
4.17k
    DGNRewind(psDGN);
1983
1984
4.17k
    psDGN->max_element_count = nMaxElements;
1985
4.17k
}