LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - dgnread.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 450 977 46.1 %
Date: 2024-11-25 13:07:18 Functions: 17 21 81.0 %

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

Generated by: LCOV version 1.14