LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/dgn - dgnread.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 457 981 46.6 %
Date: 2026-07-09 08:35:43 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        1168 : 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        1168 :     if (VSIFReadL(psDGN->abyElem, 1, 4, psDGN->fp) != 4)
      80          52 :         return FALSE;
      81             : 
      82             :     /* Is this an 0xFFFF endof file marker? */
      83        1116 :     if (psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff)
      84          30 :         return FALSE;
      85             : 
      86        1086 :     int nWords = psDGN->abyElem[2] + psDGN->abyElem[3] * 256;
      87        1086 :     int nType = psDGN->abyElem[1] & 0x7f;
      88        1086 :     int nLevel = psDGN->abyElem[0] & 0x3f;
      89             : 
      90             :     /* -------------------------------------------------------------------- */
      91             :     /*      Read the rest of the element data into the working buffer.      */
      92             :     /* -------------------------------------------------------------------- */
      93        1086 :     if (nWords * 2 + 4 >= (int)sizeof(psDGN->abyElem))
      94           0 :         return FALSE;
      95             : 
      96             :     /* coverity[tainted_data] */
      97        1086 :     if ((int)VSIFReadL(psDGN->abyElem + 4, 2, nWords, psDGN->fp) != nWords)
      98           0 :         return FALSE;
      99        1086 :     psDGN->abyElem[4 + 2 * nWords] = 0;
     100        1086 :     psDGN->abyElem[sizeof(psDGN->abyElem) - 1] = 0;
     101             : 
     102        1086 :     psDGN->nElemBytes = nWords * 2 + 4;
     103             : 
     104        1086 :     psDGN->next_element_id++;
     105             : 
     106             :     /* -------------------------------------------------------------------- */
     107             :     /*      Return requested info.                                          */
     108             :     /* -------------------------------------------------------------------- */
     109        1086 :     if (pnType != nullptr)
     110        1086 :         *pnType = nType;
     111             : 
     112        1086 :     if (pnLevel != nullptr)
     113        1086 :         *pnLevel = nLevel;
     114             : 
     115        1086 :     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         186 : 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         186 :     if (pabyRawData == nullptr)
     135         184 :         pabyRawData = psDGN->abyElem + 0;
     136             : 
     137         186 :     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         146 :         default:
     167         146 :             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         602 : static DGNElemCore *DGNProcessElement(DGNInfo *psDGN, int nType, int nLevel)
     259             : 
     260             : {
     261         602 :     DGNElemCore *psElement = nullptr;
     262             : 
     263             :     /* -------------------------------------------------------------------- */
     264             :     /*      Handle based on element type.                                   */
     265             :     /* -------------------------------------------------------------------- */
     266         602 :     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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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         102 :         case DGNT_TCB:
     808         102 :             psElement = DGNParseTCB(psDGN);
     809         102 :             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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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 = reinterpret_cast<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           1 :         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           1 :             int attr_bytes =
    1062           1 :                 psDGN->nElemBytes -
    1063           1 :                 (psDGN->abyElem[30] + psDGN->abyElem[31] * 256) * 2 - 32;
    1064           1 :             if (attr_bytes < 0)
    1065             :             {
    1066           1 :                 CPLError(CE_Failure, CPLE_AssertionFailed, "attr_bytes < 0");
    1067           1 :                 return nullptr;
    1068             :             }
    1069           0 :             int numelems = (psDGN->nElemBytes - 36 - attr_bytes) / 4;
    1070           0 :             if (numelems < 1)
    1071             :             {
    1072           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed, "numelems < 1");
    1073           0 :                 return nullptr;
    1074             :             }
    1075             :             DGNElemKnotWeight *psArray =
    1076           0 :                 static_cast<DGNElemKnotWeight *>(CPLCalloc(
    1077           0 :                     sizeof(DGNElemKnotWeight) + (numelems - 1) * sizeof(float),
    1078             :                     1));
    1079             : 
    1080           0 :             psElement = reinterpret_cast<DGNElemCore *>(psArray);
    1081           0 :             psElement->stype = DGNST_KNOT_WEIGHT;
    1082           0 :             DGNParseCore(psDGN, psElement);
    1083             : 
    1084             :             // Read array
    1085           0 :             for (int i = 0; i < numelems; i++)
    1086             :             {
    1087           0 :                 psArray->array[i] = static_cast<float>(
    1088           0 :                     1.0 * DGN_INT32(psDGN->abyElem + 36 + i * 4) /
    1089             :                     ((1UL << 31) - 1));
    1090             :             }
    1091             :         }
    1092           0 :         break;
    1093           0 :         case DGNT_SHARED_CELL_DEFN:
    1094             :         {
    1095             :             DGNElemSharedCellDefn *psShared =
    1096             :                 static_cast<DGNElemSharedCellDefn *>(
    1097           0 :                     CPLCalloc(sizeof(DGNElemSharedCellDefn), 1));
    1098           0 :             psElement = reinterpret_cast<DGNElemCore *>(psShared);
    1099           0 :             psElement->stype = DGNST_SHARED_CELL_DEFN;
    1100           0 :             DGNParseCore(psDGN, psElement);
    1101             : 
    1102           0 :             psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
    1103             :         }
    1104           0 :         break;
    1105         120 :         default:
    1106             :         {
    1107             :             psElement =
    1108         120 :                 static_cast<DGNElemCore *>(CPLCalloc(sizeof(DGNElemCore), 1));
    1109         120 :             psElement->stype = DGNST_CORE;
    1110         120 :             DGNParseCore(psDGN, psElement);
    1111             :         }
    1112         120 :         break;
    1113             :     }
    1114             : 
    1115             :     /* -------------------------------------------------------------------- */
    1116             :     /*      If the element structure type is "core" or if we are running    */
    1117             :     /*      in "capture all" mode, record the complete binary image of      */
    1118             :     /*      the element.                                                    */
    1119             :     /* -------------------------------------------------------------------- */
    1120         601 :     if (psElement->stype == DGNST_CORE ||
    1121         144 :         (psDGN->options & DGNO_CAPTURE_RAW_DATA))
    1122             :     {
    1123         491 :         psElement->raw_bytes = psDGN->nElemBytes;
    1124         491 :         psElement->raw_data =
    1125         491 :             static_cast<unsigned char *>(CPLMalloc(psElement->raw_bytes));
    1126             : 
    1127         491 :         memcpy(psElement->raw_data, psDGN->abyElem, psElement->raw_bytes);
    1128             :     }
    1129             : 
    1130             :     /* -------------------------------------------------------------------- */
    1131             :     /*      Collect some additional generic information.                    */
    1132             :     /* -------------------------------------------------------------------- */
    1133         601 :     psElement->element_id = psDGN->next_element_id - 1;
    1134             : 
    1135         601 :     psElement->offset =
    1136         601 :         static_cast<int>(VSIFTellL(psDGN->fp)) - psDGN->nElemBytes;
    1137         601 :     psElement->size = psDGN->nElemBytes;
    1138             : 
    1139         601 :     return psElement;
    1140             : }
    1141             : 
    1142             : /************************************************************************/
    1143             : /*                           DGNReadElement()                           */
    1144             : /************************************************************************/
    1145             : 
    1146             : /**
    1147             :  * Read a DGN element.
    1148             :  *
    1149             :  * This function will return the next element in the file, starting with the
    1150             :  * first.  It is affected by DGNGotoElement() calls.
    1151             :  *
    1152             :  * The element is read into a structure which includes the DGNElemCore
    1153             :  * structure.  It is expected that applications will inspect the stype
    1154             :  * field of the returned DGNElemCore and use it to cast the pointer to the
    1155             :  * appropriate element structure type such as DGNElemMultiPoint.
    1156             :  *
    1157             :  * @param hDGN the handle of the file to read from.
    1158             :  *
    1159             :  * @return pointer to element structure, or NULL on EOF or processing error.
    1160             :  * The structure should be freed with DGNFreeElement() when no longer needed.
    1161             :  */
    1162             : 
    1163         639 : DGNElemCore *DGNReadElement(DGNHandle hDGN)
    1164             : 
    1165             : {
    1166         639 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1167         639 :     int nType = 0;
    1168         639 :     int nLevel = 0;
    1169         639 :     bool bInsideFilter = false;
    1170             : 
    1171             :     /* -------------------------------------------------------------------- */
    1172             :     /*      Load the element data into the current buffer.  If a spatial    */
    1173             :     /*      filter is in effect, loop until we get something within our     */
    1174             :     /*      spatial constraints.                                            */
    1175             :     /* -------------------------------------------------------------------- */
    1176           3 :     do
    1177             :     {
    1178         642 :         bInsideFilter = true;
    1179             : 
    1180         642 :         if (!DGNLoadRawElement(psDGN, &nType, &nLevel))
    1181          37 :             return nullptr;
    1182             : 
    1183         605 :         if (psDGN->has_spatial_filter)
    1184             :         {
    1185          15 :             if (!psDGN->sf_converted_to_uor)
    1186           1 :                 DGNSpatialFilterToUOR(psDGN);
    1187             : 
    1188          15 :             GUInt32 nXMin = 0;
    1189          15 :             GUInt32 nXMax = 0;
    1190          15 :             GUInt32 nYMin = 0;
    1191          15 :             GUInt32 nYMax = 0;
    1192          15 :             if (!DGNGetRawExtents(psDGN, nType, nullptr, &nXMin, &nYMin,
    1193             :                                   nullptr, &nXMax, &nYMax, nullptr))
    1194             :             {
    1195             :                 /* If we don't have spatial characteristics for the element
    1196             :                    we will pass it through. */
    1197          11 :                 bInsideFilter = true;
    1198             :             }
    1199           4 :             else if (nXMin > psDGN->sf_max_x || nYMin > psDGN->sf_max_y ||
    1200           2 :                      nXMax < psDGN->sf_min_x || nYMax < psDGN->sf_min_y)
    1201             :             {
    1202           3 :                 bInsideFilter = false;
    1203             :             }
    1204             : 
    1205             :             /*
    1206             :             ** We want to select complex elements based on the extents of
    1207             :             ** the header, not the individual elements.
    1208             :             */
    1209          15 :             if (nType == DGNT_COMPLEX_CHAIN_HEADER ||
    1210          15 :                 nType == DGNT_COMPLEX_SHAPE_HEADER)
    1211             :             {
    1212           0 :                 psDGN->in_complex_group = true;
    1213           0 :                 psDGN->select_complex_group = bInsideFilter;
    1214             :             }
    1215          15 :             else if (psDGN->abyElem[0] & 0x80 /* complex flag set */)
    1216             :             {
    1217           0 :                 if (psDGN->in_complex_group)
    1218           0 :                     bInsideFilter = psDGN->select_complex_group;
    1219             :             }
    1220             :             else
    1221             :             {
    1222          15 :                 psDGN->in_complex_group = false;
    1223             :             }
    1224             :         }
    1225         605 :     } while (!bInsideFilter);
    1226             : 
    1227             :     /* -------------------------------------------------------------------- */
    1228             :     /*      Convert into an element structure.                              */
    1229             :     /* -------------------------------------------------------------------- */
    1230         602 :     DGNElemCore *psElement = DGNProcessElement(psDGN, nType, nLevel);
    1231             : 
    1232         602 :     return psElement;
    1233             : }
    1234             : 
    1235             : /************************************************************************/
    1236             : /*                       DGNElemTypeHasDispHdr()                        */
    1237             : /************************************************************************/
    1238             : 
    1239             : /**
    1240             :  * Does element type have display header.
    1241             :  *
    1242             :  * @param nElemType element type (0-63) to test.
    1243             :  *
    1244             :  * @return TRUE if elements of passed in type have a display header after the
    1245             :  * core element header, or FALSE otherwise.
    1246             :  */
    1247             : 
    1248         792 : int DGNElemTypeHasDispHdr(int nElemType)
    1249             : 
    1250             : {
    1251         792 :     switch (nElemType)
    1252             :     {
    1253         214 :         case 0:
    1254             :         case DGNT_TCB:
    1255             :         case DGNT_CELL_LIBRARY:
    1256             :         case DGNT_LEVEL_SYMBOLOGY:
    1257             :         case 32:
    1258             :         case 44:
    1259             :         case 48:
    1260             :         case 49:
    1261             :         case 50:
    1262             :         case 51:
    1263             :         case 57:
    1264             :         case 60:
    1265             :         case 61:
    1266             :         case 62:
    1267             :         case 63:
    1268         214 :             return FALSE;
    1269             : 
    1270         578 :         default:
    1271         578 :             return TRUE;
    1272             :     }
    1273             : }
    1274             : 
    1275             : /************************************************************************/
    1276             : /*                            DGNParseCore()                            */
    1277             : /************************************************************************/
    1278             : 
    1279         653 : int DGNParseCore(DGNInfo *psDGN, DGNElemCore *psElement)
    1280             : 
    1281             : {
    1282         653 :     GByte *psData = psDGN->abyElem + 0;
    1283             : 
    1284         653 :     psElement->level = psData[0] & 0x3f;
    1285         653 :     psElement->complex = psData[0] & 0x80;
    1286         653 :     psElement->deleted = psData[1] & 0x80;
    1287         653 :     psElement->type = psData[1] & 0x7f;
    1288             : 
    1289         653 :     if (psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr(psElement->type))
    1290             :     {
    1291         439 :         psElement->graphic_group = psData[28] + psData[29] * 256;
    1292         439 :         psElement->properties = psData[32] + psData[33] * 256;
    1293         439 :         psElement->style = psData[34] & 0x7;
    1294         439 :         psElement->weight = (psData[34] & 0xf8) >> 3;
    1295         439 :         psElement->color = psData[35];
    1296             :     }
    1297             :     else
    1298             :     {
    1299         214 :         psElement->graphic_group = 0;
    1300         214 :         psElement->properties = 0;
    1301         214 :         psElement->style = 0;
    1302         214 :         psElement->weight = 0;
    1303         214 :         psElement->color = 0;
    1304             :     }
    1305             : 
    1306         653 :     if (psElement->properties & DGNPF_ATTRIBUTES)
    1307             :     {
    1308          25 :         const int nAttIndex = psData[30] + psData[31] * 256;
    1309             : 
    1310          25 :         psElement->attr_bytes = psDGN->nElemBytes - nAttIndex * 2 - 32;
    1311          25 :         if (psElement->attr_bytes > 0)
    1312             :         {
    1313          25 :             psElement->attr_data =
    1314          25 :                 static_cast<unsigned char *>(CPLMalloc(psElement->attr_bytes));
    1315          25 :             memcpy(psElement->attr_data, psData + nAttIndex * 2 + 32,
    1316          25 :                    psElement->attr_bytes);
    1317             :         }
    1318             :         else
    1319             :         {
    1320           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1321             :                      "Computed %d bytes for attribute info on element,\n"
    1322             :                      "perhaps this element type doesn't really have a disphdr?",
    1323             :                      psElement->attr_bytes);
    1324           0 :             psElement->attr_bytes = 0;
    1325             :         }
    1326             :     }
    1327             : 
    1328         653 :     return TRUE;
    1329             : }
    1330             : 
    1331             : /************************************************************************/
    1332             : /*                         DGNParseColorTable()                         */
    1333             : /************************************************************************/
    1334             : 
    1335           0 : static DGNElemCore *DGNParseColorTable(DGNInfo *psDGN)
    1336             : 
    1337             : {
    1338             :     DGNElemColorTable *psColorTable = static_cast<DGNElemColorTable *>(
    1339           0 :         CPLCalloc(sizeof(DGNElemColorTable), 1));
    1340           0 :     DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psColorTable);
    1341           0 :     psElement->stype = DGNST_COLORTABLE;
    1342             : 
    1343           0 :     DGNParseCore(psDGN, psElement);
    1344             : 
    1345           0 :     psColorTable->screen_flag = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
    1346             : 
    1347           0 :     memcpy(psColorTable->color_info[255], psDGN->abyElem + 38, 3);
    1348           0 :     memcpy(psColorTable->color_info, psDGN->abyElem + 41, 765);
    1349             : 
    1350             :     // We used to only install a color table as the default color
    1351             :     // table if it was the first in the file.  But apparently we should
    1352             :     // really be using the last one.  This doesn't necessarily accomplish
    1353             :     // that either if the elements are being read out of order but it will
    1354             :     // usually do better at least.
    1355           0 :     memcpy(psDGN->color_table, psColorTable->color_info, 768);
    1356           0 :     psDGN->got_color_table = 1;
    1357             : 
    1358           0 :     return psElement;
    1359             : }
    1360             : 
    1361             : /************************************************************************/
    1362             : /*                           DGNParseTagSet()                           */
    1363             : /************************************************************************/
    1364             : 
    1365           0 : static DGNElemCore *DGNParseTagSet(DGNInfo *psDGN)
    1366             : 
    1367             : {
    1368             :     DGNElemTagSet *psTagSet =
    1369           0 :         static_cast<DGNElemTagSet *>(CPLCalloc(sizeof(DGNElemTagSet), 1));
    1370           0 :     DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTagSet);
    1371           0 :     psElement->stype = DGNST_TAG_SET;
    1372             : 
    1373           0 :     DGNParseCore(psDGN, psElement);
    1374             : 
    1375             :     /* -------------------------------------------------------------------- */
    1376             :     /*      Parse the overall information.                                  */
    1377             :     /* -------------------------------------------------------------------- */
    1378           0 :     psTagSet->tagCount = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
    1379           0 :     psTagSet->flags = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
    1380           0 :     psTagSet->tagSetName = CPLStrdup((const char *)(psDGN->abyElem + 48));
    1381             : 
    1382             :     /* -------------------------------------------------------------------- */
    1383             :     /*      Get the tag set number out of the attributes, if available.     */
    1384             :     /* -------------------------------------------------------------------- */
    1385           0 :     psTagSet->tagSet = -1;
    1386             : 
    1387           0 :     if (psElement->attr_bytes >= 8 && psElement->attr_data[0] == 0x03 &&
    1388           0 :         psElement->attr_data[1] == 0x10 && psElement->attr_data[2] == 0x2f &&
    1389           0 :         psElement->attr_data[3] == 0x7d)
    1390           0 :         psTagSet->tagSet =
    1391           0 :             psElement->attr_data[4] + psElement->attr_data[5] * 256;
    1392             : 
    1393             :     /* -------------------------------------------------------------------- */
    1394             :     /*      Parse each of the tag definitions.                              */
    1395             :     /* -------------------------------------------------------------------- */
    1396           0 :     psTagSet->tagList = static_cast<DGNTagDef *>(
    1397           0 :         CPLCalloc(sizeof(DGNTagDef), psTagSet->tagCount));
    1398             : 
    1399           0 :     size_t nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1;
    1400             : 
    1401           0 :     for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
    1402             :     {
    1403           0 :         DGNTagDef *tagDef = psTagSet->tagList + iTag;
    1404             : 
    1405             :         // Check the buffer is large enough to read all tagDef components
    1406           0 :         size_t nDataOffsetEnd = nDataOffset;
    1407           0 :         if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
    1408             :         {
    1409           0 :             nDataOffsetEnd +=
    1410           0 :                 strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2;
    1411             :         }
    1412           0 :         if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
    1413             :         {
    1414           0 :             nDataOffsetEnd +=
    1415           0 :                 strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2 + 5;
    1416           0 :             if (tagDef->type == 1)
    1417             :             {
    1418           0 :                 nDataOffsetEnd += strlen(tagDef->defaultValue.string) + 1;
    1419             :             }
    1420           0 :             else if (tagDef->type == 3 || tagDef->type == 5)
    1421             :             {
    1422           0 :                 nDataOffsetEnd += 4;
    1423             :             }
    1424           0 :             else if (tagDef->type == 4)
    1425             :             {
    1426           0 :                 nDataOffsetEnd += 8;
    1427             :             }
    1428             :             else
    1429             :             {
    1430           0 :                 nDataOffsetEnd += 4;
    1431             :             }
    1432             :         }
    1433           0 :         if (nDataOffsetEnd > static_cast<size_t>(psDGN->nElemBytes))
    1434             :         {
    1435           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    1436             :                      "nDataOffset >= static_cast<size_t>(psDGN->nElemBytes)");
    1437           0 :             DGNFreeElement(psDGN, psElement);
    1438           0 :             return nullptr;
    1439             :         }
    1440             : 
    1441             :         /* collect tag name. */
    1442           0 :         tagDef->name = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
    1443           0 :         nDataOffset += strlen(tagDef->name) + 1;
    1444             : 
    1445             :         /* Get tag id */
    1446           0 :         tagDef->id =
    1447           0 :             psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
    1448           0 :         nDataOffset += 2;
    1449             : 
    1450             :         /* Get User Prompt */
    1451           0 :         tagDef->prompt = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
    1452           0 :         nDataOffset += strlen(tagDef->prompt) + 1;
    1453             : 
    1454             :         /* Get type */
    1455           0 :         tagDef->type =
    1456           0 :             psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
    1457           0 :         nDataOffset += 2;
    1458             : 
    1459             :         /* skip five zeros */
    1460           0 :         nDataOffset += 5;
    1461             : 
    1462             :         /* Get the default */
    1463           0 :         if (tagDef->type == 1)
    1464             :         {
    1465           0 :             tagDef->defaultValue.string =
    1466           0 :                 CPLStrdup((char *)psDGN->abyElem + nDataOffset);
    1467           0 :             nDataOffset += strlen(tagDef->defaultValue.string) + 1;
    1468             :         }
    1469           0 :         else if (tagDef->type == 3 || tagDef->type == 5)
    1470             :         {
    1471           0 :             memcpy(&(tagDef->defaultValue.integer),
    1472           0 :                    psDGN->abyElem + nDataOffset, 4);
    1473           0 :             CPL_LSBPTR32(&(tagDef->defaultValue.integer));
    1474           0 :             nDataOffset += 4;
    1475             :         }
    1476           0 :         else if (tagDef->type == 4)
    1477             :         {
    1478           0 :             memcpy(&(tagDef->defaultValue.real), psDGN->abyElem + nDataOffset,
    1479             :                    8);
    1480           0 :             DGN2IEEEDouble(&(tagDef->defaultValue.real));
    1481           0 :             nDataOffset += 8;
    1482             :         }
    1483             :         else
    1484           0 :             nDataOffset += 4;
    1485             :     }
    1486           0 :     return psElement;
    1487             : }
    1488             : 
    1489             : /************************************************************************/
    1490             : /*                            DGNParseTCB()                             */
    1491             : /************************************************************************/
    1492             : 
    1493         154 : static DGNElemCore *DGNParseTCB(DGNInfo *psDGN)
    1494             : 
    1495             : {
    1496             :     DGNElemTCB *psTCB =
    1497         154 :         static_cast<DGNElemTCB *>(CPLCalloc(sizeof(DGNElemTCB), 1));
    1498         154 :     DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTCB);
    1499         154 :     psElement->stype = DGNST_TCB;
    1500         154 :     DGNParseCore(psDGN, psElement);
    1501             : 
    1502         154 :     if (psDGN->abyElem[1214] & 0x40)
    1503          49 :         psTCB->dimension = 3;
    1504             :     else
    1505         105 :         psTCB->dimension = 2;
    1506             : 
    1507         154 :     psTCB->subunits_per_master =
    1508         154 :         static_cast<long>(DGN_INT32(psDGN->abyElem + 1112));
    1509             : 
    1510         154 :     psTCB->master_units[0] = (char)psDGN->abyElem[1120];
    1511         154 :     psTCB->master_units[1] = (char)psDGN->abyElem[1121];
    1512         154 :     psTCB->master_units[2] = '\0';
    1513             : 
    1514         154 :     psTCB->uor_per_subunit =
    1515         154 :         static_cast<long>(DGN_INT32(psDGN->abyElem + 1116));
    1516             : 
    1517         154 :     psTCB->sub_units[0] = (char)psDGN->abyElem[1122];
    1518         154 :     psTCB->sub_units[1] = (char)psDGN->abyElem[1123];
    1519         154 :     psTCB->sub_units[2] = '\0';
    1520             : 
    1521             :     /* Get global origin */
    1522         154 :     memcpy(&(psTCB->origin_x), psDGN->abyElem + 1240, 8);
    1523         154 :     memcpy(&(psTCB->origin_y), psDGN->abyElem + 1248, 8);
    1524         154 :     memcpy(&(psTCB->origin_z), psDGN->abyElem + 1256, 8);
    1525             : 
    1526             :     /* Transform to IEEE */
    1527         154 :     DGN2IEEEDouble(&(psTCB->origin_x));
    1528         154 :     DGN2IEEEDouble(&(psTCB->origin_y));
    1529         154 :     DGN2IEEEDouble(&(psTCB->origin_z));
    1530             : 
    1531             :     /* Convert from UORs to master units. */
    1532         154 :     if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
    1533             :     {
    1534         138 :         psTCB->origin_x = psTCB->origin_x /
    1535         138 :                           (psTCB->uor_per_subunit * psTCB->subunits_per_master);
    1536         138 :         psTCB->origin_y = psTCB->origin_y /
    1537         138 :                           (psTCB->uor_per_subunit * psTCB->subunits_per_master);
    1538         138 :         psTCB->origin_z = psTCB->origin_z /
    1539         138 :                           (psTCB->uor_per_subunit * psTCB->subunits_per_master);
    1540             :     }
    1541             : 
    1542         154 :     if (!psDGN->got_tcb)
    1543             :     {
    1544          79 :         psDGN->got_tcb = true;
    1545          79 :         psDGN->dimension = psTCB->dimension;
    1546          79 :         psDGN->origin_x = psTCB->origin_x;
    1547          79 :         psDGN->origin_y = psTCB->origin_y;
    1548          79 :         psDGN->origin_z = psTCB->origin_z;
    1549             : 
    1550          79 :         if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
    1551          78 :             psDGN->scale =
    1552          78 :                 1.0 / (psTCB->uor_per_subunit * psTCB->subunits_per_master);
    1553             :     }
    1554             : 
    1555             :     /* Collect views */
    1556        1386 :     for (int iView = 0; iView < 8; iView++)
    1557             :     {
    1558        1232 :         unsigned char *pabyRawView = psDGN->abyElem + 46 + iView * 118;
    1559        1232 :         DGNViewInfo *psView = psTCB->views + iView;
    1560             : 
    1561        1232 :         psView->flags = pabyRawView[0] + pabyRawView[1] * 256;
    1562        1232 :         memcpy(psView->levels, pabyRawView + 2, 8);
    1563             : 
    1564        1232 :         psView->origin.x = DGN_INT32(pabyRawView + 10);
    1565        1232 :         psView->origin.y = DGN_INT32(pabyRawView + 14);
    1566        1232 :         psView->origin.z = DGN_INT32(pabyRawView + 18);
    1567             : 
    1568        1232 :         DGNTransformPoint(psDGN, &(psView->origin));
    1569             : 
    1570        1232 :         psView->delta.x = DGN_INT32(pabyRawView + 22);
    1571        1232 :         psView->delta.y = DGN_INT32(pabyRawView + 26);
    1572        1232 :         psView->delta.z = DGN_INT32(pabyRawView + 30);
    1573             : 
    1574        1232 :         psView->delta.x *= psDGN->scale;
    1575        1232 :         psView->delta.y *= psDGN->scale;
    1576        1232 :         psView->delta.z *= psDGN->scale;
    1577             : 
    1578        1232 :         memcpy(psView->transmatrx, pabyRawView + 34, sizeof(double) * 9);
    1579       12320 :         for (int i = 0; i < 9; i++)
    1580       11088 :             DGN2IEEEDouble(psView->transmatrx + i);
    1581             : 
    1582        1232 :         memcpy(&(psView->conversion), pabyRawView + 106, sizeof(double));
    1583        1232 :         DGN2IEEEDouble(&(psView->conversion));
    1584             : 
    1585        1232 :         psView->activez =
    1586        1232 :             static_cast<unsigned long>(DGN_INT32(pabyRawView + 114));
    1587             :     }
    1588             : 
    1589         154 :     return psElement;
    1590             : }
    1591             : 
    1592             : /************************************************************************/
    1593             : /*                           DGNFreeElement()                           */
    1594             : /************************************************************************/
    1595             : 
    1596             : /**
    1597             :  * Free an element structure.
    1598             :  *
    1599             :  * This function will deallocate all resources associated with any element
    1600             :  * structure returned by DGNReadElement().
    1601             :  *
    1602             :  * @param hDGN handle to file from which the element was read.
    1603             :  * @param psElement the element structure returned by DGNReadElement().
    1604             :  */
    1605             : 
    1606         948 : void DGNFreeElement(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement)
    1607             : {
    1608         948 :     if (psElement->attr_data != nullptr)
    1609          70 :         VSIFree(psElement->attr_data);
    1610             : 
    1611         948 :     if (psElement->raw_data != nullptr)
    1612         786 :         VSIFree(psElement->raw_data);
    1613             : 
    1614         948 :     if (psElement->stype == DGNST_TAG_SET)
    1615             :     {
    1616           0 :         DGNElemTagSet *psTagSet = reinterpret_cast<DGNElemTagSet *>(psElement);
    1617           0 :         CPLFree(psTagSet->tagSetName);
    1618             : 
    1619           0 :         for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
    1620             :         {
    1621           0 :             CPLFree(psTagSet->tagList[iTag].name);
    1622           0 :             CPLFree(psTagSet->tagList[iTag].prompt);
    1623             : 
    1624           0 :             if (psTagSet->tagList[iTag].type == 1)
    1625           0 :                 CPLFree(psTagSet->tagList[iTag].defaultValue.string);
    1626             :         }
    1627           0 :         CPLFree(psTagSet->tagList);
    1628             :     }
    1629         948 :     else if (psElement->stype == DGNST_TAG_VALUE)
    1630             :     {
    1631           0 :         DGNElemTagValue *psTagValue =
    1632             :             reinterpret_cast<DGNElemTagValue *>(psElement);
    1633           0 :         if (psTagValue->tagType == 1)
    1634           0 :             CPLFree(psTagValue->tagValue.string);
    1635             :     }
    1636             : 
    1637         948 :     CPLFree(psElement);
    1638         948 : }
    1639             : 
    1640             : /************************************************************************/
    1641             : /*                             DGNRewind()                              */
    1642             : /************************************************************************/
    1643             : 
    1644             : /**
    1645             :  * Rewind element reading.
    1646             :  *
    1647             :  * Rewind the indicated DGN file, so the next element read with
    1648             :  * DGNReadElement() will be the first.  Does not require indexing like
    1649             :  * the more general DGNReadElement() function.
    1650             :  *
    1651             :  * @param hDGN handle to file.
    1652             :  */
    1653             : 
    1654         114 : void DGNRewind(DGNHandle hDGN)
    1655             : 
    1656             : {
    1657         114 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1658             : 
    1659         114 :     VSIRewindL(psDGN->fp);
    1660             : 
    1661         114 :     psDGN->next_element_id = 0;
    1662         114 :     psDGN->in_complex_group = false;
    1663         114 : }
    1664             : 
    1665             : /************************************************************************/
    1666             : /*                         DGNTransformPoint()                          */
    1667             : /************************************************************************/
    1668             : 
    1669        1400 : void DGNTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
    1670             : 
    1671             : {
    1672        1400 :     psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x;
    1673        1400 :     psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y;
    1674        1400 :     psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z;
    1675        1400 : }
    1676             : 
    1677             : /************************************************************************/
    1678             : /*                      DGNInverseTransformPoint()                      */
    1679             : /************************************************************************/
    1680             : 
    1681           2 : void DGNInverseTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
    1682             : 
    1683             : {
    1684           2 :     psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale;
    1685           2 :     psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale;
    1686           2 :     psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale;
    1687             : 
    1688           2 :     psPoint->x = std::max(-2147483647.0, std::min(2147483647.0, psPoint->x));
    1689           2 :     psPoint->y = std::max(-2147483647.0, std::min(2147483647.0, psPoint->y));
    1690           2 :     psPoint->z = std::max(-2147483647.0, std::min(2147483647.0, psPoint->z));
    1691           2 : }
    1692             : 
    1693             : /************************************************************************/
    1694             : /*                   DGNInverseTransformPointToInt()                    */
    1695             : /************************************************************************/
    1696             : 
    1697         293 : void DGNInverseTransformPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
    1698             :                                    unsigned char *pabyTarget)
    1699             : 
    1700             : {
    1701         293 :     double adfCT[3] = {(psPoint->x + psDGN->origin_x) / psDGN->scale,
    1702         293 :                        (psPoint->y + psDGN->origin_y) / psDGN->scale,
    1703         293 :                        (psPoint->z + psDGN->origin_z) / psDGN->scale};
    1704             : 
    1705         293 :     const int nIter = std::min(3, psDGN->dimension);
    1706         969 :     for (int i = 0; i < nIter; i++)
    1707             :     {
    1708         676 :         GInt32 nCTI = static_cast<GInt32>(
    1709         676 :             std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
    1710             :         unsigned char abyCTI[4];
    1711         676 :         memcpy(abyCTI, &nCTI, sizeof(GInt32));
    1712             : 
    1713             : #ifdef WORDS_BIGENDIAN
    1714             :         pabyTarget[i * 4 + 0] = abyCTI[1];
    1715             :         pabyTarget[i * 4 + 1] = abyCTI[0];
    1716             :         pabyTarget[i * 4 + 2] = abyCTI[3];
    1717             :         pabyTarget[i * 4 + 3] = abyCTI[2];
    1718             : #else
    1719         676 :         pabyTarget[i * 4 + 3] = abyCTI[1];
    1720         676 :         pabyTarget[i * 4 + 2] = abyCTI[0];
    1721         676 :         pabyTarget[i * 4 + 1] = abyCTI[3];
    1722         676 :         pabyTarget[i * 4 + 0] = abyCTI[2];
    1723             : #endif
    1724             :     }
    1725         293 : }
    1726             : 
    1727             : /************************************************************************/
    1728             : /*                             DGNLoadTCB()                             */
    1729             : /************************************************************************/
    1730             : 
    1731             : /**
    1732             :  * Load TCB if not already loaded.
    1733             :  *
    1734             :  * This function will load the TCB element if it is not already loaded.
    1735             :  * It is used primarily to ensure the TCB is loaded before doing any operations
    1736             :  * that require TCB values (like creating new elements).
    1737             :  *
    1738             :  * @return FALSE on failure or TRUE on success.
    1739             :  */
    1740             : 
    1741         296 : int DGNLoadTCB(DGNHandle hDGN)
    1742             : 
    1743             : {
    1744         296 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1745             : 
    1746         296 :     if (psDGN->got_tcb)
    1747         262 :         return TRUE;
    1748             : 
    1749          68 :     while (!psDGN->got_tcb)
    1750             :     {
    1751          34 :         DGNElemCore *psElem = DGNReadElement(hDGN);
    1752          34 :         if (psElem == nullptr)
    1753             :         {
    1754           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1755             :                      "DGNLoadTCB() - unable to find TCB in file.");
    1756           0 :             return FALSE;
    1757             :         }
    1758          34 :         DGNFreeElement(hDGN, psElem);
    1759             :     }
    1760             : 
    1761          34 :     return TRUE;
    1762             : }
    1763             : 
    1764             : /************************************************************************/
    1765             : /*                         DGNGetElementIndex()                         */
    1766             : /************************************************************************/
    1767             : 
    1768             : /**
    1769             :  * Fetch element index.
    1770             :  *
    1771             :  * This function will return an array with brief information about every
    1772             :  * element in a DGN file.  It requires one pass through the entire file to
    1773             :  * generate (this is not repeated on subsequent calls).
    1774             :  *
    1775             :  * The returned array of DGNElementInfo structures contain the level, type,
    1776             :  * stype, and other flags for each element in the file.  This can facilitate
    1777             :  * application level code representing the number of elements of various types
    1778             :  * efficiently.
    1779             :  *
    1780             :  * Note that while building the index requires one pass through the whole file,
    1781             :  * it does not generally request much processing for each element.
    1782             :  *
    1783             :  * @param hDGN the file to get an index for.
    1784             :  * @param pnElementCount the integer to put the total element count into.
    1785             :  *
    1786             :  * @return a pointer to an internal array of DGNElementInfo structures (there
    1787             :  * will be *pnElementCount entries in the array), or NULL on failure.  The
    1788             :  * returned array should not be modified or freed, and will last only as long
    1789             :  * as the DGN file remains open.
    1790             :  */
    1791             : 
    1792          38 : const DGNElementInfo *DGNGetElementIndex(DGNHandle hDGN, int *pnElementCount)
    1793             : 
    1794             : {
    1795          38 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1796             : 
    1797          38 :     DGNBuildIndex(psDGN);
    1798             : 
    1799          38 :     if (pnElementCount != nullptr)
    1800           0 :         *pnElementCount = psDGN->element_count;
    1801             : 
    1802          38 :     return psDGN->element_index;
    1803             : }
    1804             : 
    1805             : /************************************************************************/
    1806             : /*                           DGNGetExtents()                            */
    1807             : /************************************************************************/
    1808             : 
    1809             : /**
    1810             :  * Fetch overall file extents.
    1811             :  *
    1812             :  * The extents are collected for each element while building an index, so
    1813             :  * if an index has not already been built, it will be built when
    1814             :  * DGNGetExtents() is called.
    1815             :  *
    1816             :  * The Z min/max values are generally meaningless (0 and 0xffffffff in uor
    1817             :  * space).
    1818             :  *
    1819             :  * @param hDGN the file to get extents for.
    1820             :  * @param padfExtents pointer to an array of six doubles into which are loaded
    1821             :  * the values xmin, ymin, zmin, xmax, ymax, and zmax.
    1822             :  *
    1823             :  * @return TRUE on success or FALSE on failure.
    1824             :  */
    1825             : 
    1826           0 : int DGNGetExtents(DGNHandle hDGN, double *padfExtents)
    1827             : 
    1828             : {
    1829           0 :     DGNInfo *psDGN = (DGNInfo *)hDGN;
    1830             : 
    1831           0 :     DGNBuildIndex(psDGN);
    1832             : 
    1833           0 :     if (!psDGN->got_bounds)
    1834           0 :         return FALSE;
    1835             : 
    1836           0 :     DGNPoint sMin = {psDGN->min_x - 2147483648.0, psDGN->min_y - 2147483648.0,
    1837           0 :                      psDGN->min_z - 2147483648.0};
    1838             : 
    1839           0 :     DGNTransformPoint(psDGN, &sMin);
    1840             : 
    1841           0 :     padfExtents[0] = sMin.x;
    1842           0 :     padfExtents[1] = sMin.y;
    1843           0 :     padfExtents[2] = sMin.z;
    1844             : 
    1845           0 :     DGNPoint sMax = {psDGN->max_x - 2147483648.0, psDGN->max_y - 2147483648.0,
    1846           0 :                      psDGN->max_z - 2147483648.0};
    1847             : 
    1848           0 :     DGNTransformPoint(psDGN, &sMax);
    1849             : 
    1850           0 :     padfExtents[3] = sMax.x;
    1851           0 :     padfExtents[4] = sMax.y;
    1852           0 :     padfExtents[5] = sMax.z;
    1853             : 
    1854           0 :     return TRUE;
    1855             : }
    1856             : 
    1857             : /************************************************************************/
    1858             : /*                           DGNBuildIndex()                            */
    1859             : /************************************************************************/
    1860             : 
    1861         367 : void DGNBuildIndex(DGNInfo *psDGN)
    1862             : 
    1863             : {
    1864         367 :     if (psDGN->index_built)
    1865         322 :         return;
    1866             : 
    1867          45 :     int nType = 0;
    1868          45 :     int nLevel = 0;
    1869          45 :     GUInt32 anRegion[6] = {};
    1870             : 
    1871          45 :     psDGN->index_built = true;
    1872             : 
    1873          45 :     DGNRewind(psDGN);
    1874             : 
    1875          45 :     int nMaxElements = 0;
    1876             : 
    1877          45 :     vsi_l_offset nLastOffset = VSIFTellL(psDGN->fp);
    1878         231 :     while (DGNLoadRawElement(psDGN, &nType, &nLevel))
    1879             :     {
    1880         186 :         if (psDGN->element_count == nMaxElements)
    1881             :         {
    1882          45 :             nMaxElements = (int)(nMaxElements * 1.5) + 500;
    1883             : 
    1884          45 :             psDGN->element_index = (DGNElementInfo *)CPLRealloc(
    1885          45 :                 psDGN->element_index, nMaxElements * sizeof(DGNElementInfo));
    1886             :         }
    1887             : 
    1888         186 :         DGNElementInfo *psEI = psDGN->element_index + psDGN->element_count;
    1889         186 :         psEI->level = (unsigned char)nLevel;
    1890         186 :         psEI->type = (unsigned char)nType;
    1891         186 :         psEI->flags = 0;
    1892         186 :         psEI->offset = nLastOffset;
    1893             : 
    1894         186 :         if (psDGN->abyElem[0] & 0x80)
    1895          17 :             psEI->flags |= DGNEIF_COMPLEX;
    1896             : 
    1897         186 :         if (psDGN->abyElem[1] & 0x80)
    1898           0 :             psEI->flags |= DGNEIF_DELETED;
    1899             : 
    1900         186 :         if (nType == DGNT_LINE || nType == DGNT_LINE_STRING ||
    1901         175 :             nType == DGNT_SHAPE || nType == DGNT_CURVE ||
    1902         167 :             nType == DGNT_BSPLINE_POLE)
    1903          19 :             psEI->stype = DGNST_MULTIPOINT;
    1904             : 
    1905         167 :         else if (nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE)
    1906             :         {
    1907           0 :             DGNElemCore *psCT = DGNParseColorTable(psDGN);
    1908           0 :             DGNFreeElement((DGNHandle)psDGN, psCT);
    1909           0 :             psEI->stype = DGNST_COLORTABLE;
    1910             :         }
    1911         167 :         else if (nType == DGNT_ELLIPSE || nType == DGNT_ARC)
    1912           7 :             psEI->stype = DGNST_ARC;
    1913             : 
    1914         160 :         else if (nType == DGNT_COMPLEX_SHAPE_HEADER ||
    1915         160 :                  nType == DGNT_COMPLEX_CHAIN_HEADER ||
    1916         159 :                  nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER)
    1917           1 :             psEI->stype = DGNST_COMPLEX_HEADER;
    1918             : 
    1919         159 :         else if (nType == DGNT_TEXT)
    1920          10 :             psEI->stype = DGNST_TEXT;
    1921             : 
    1922         149 :         else if (nType == DGNT_TAG_VALUE)
    1923           0 :             psEI->stype = DGNST_TAG_VALUE;
    1924             : 
    1925         149 :         else if (nType == DGNT_APPLICATION_ELEM)
    1926             :         {
    1927          69 :             if (nLevel == 24)
    1928           0 :                 psEI->stype = DGNST_TAG_SET;
    1929             :             else
    1930          69 :                 psEI->stype = DGNST_CORE;
    1931             :         }
    1932          80 :         else if (nType == DGNT_TCB)
    1933             :         {
    1934          52 :             DGNElemCore *psTCB = DGNParseTCB(psDGN);
    1935          52 :             DGNFreeElement((DGNHandle)psDGN, psTCB);
    1936          52 :             psEI->stype = DGNST_TCB;
    1937             :         }
    1938          28 :         else if (nType == DGNT_CONE)
    1939           0 :             psEI->stype = DGNST_CONE;
    1940             :         else
    1941          28 :             psEI->stype = DGNST_CORE;
    1942             : 
    1943         558 :         if (!(psEI->flags & DGNEIF_DELETED) &&
    1944         355 :             !(psEI->flags & DGNEIF_COMPLEX) &&
    1945         169 :             DGNGetRawExtents(psDGN, nType, nullptr, anRegion + 0, anRegion + 1,
    1946             :                              anRegion + 2, anRegion + 3, anRegion + 4,
    1947             :                              anRegion + 5))
    1948             :         {
    1949             : #ifdef notdef
    1950             :             printf("panRegion[%d]=%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n", /*ok*/
    1951             :                    psDGN->element_count, anRegion[0] - 2147483648.0,
    1952             :                    anRegion[1] - 2147483648.0, anRegion[2] - 2147483648.0,
    1953             :                    anRegion[3] - 2147483648.0, anRegion[4] - 2147483648.0,
    1954             :                    anRegion[5] - 2147483648.0);
    1955             : #endif
    1956          34 :             if (psDGN->got_bounds)
    1957             :             {
    1958          24 :                 psDGN->min_x = std::min(psDGN->min_x, anRegion[0]);
    1959          24 :                 psDGN->min_y = std::min(psDGN->min_y, anRegion[1]);
    1960          24 :                 psDGN->min_z = std::min(psDGN->min_z, anRegion[2]);
    1961          24 :                 psDGN->max_x = std::max(psDGN->max_x, anRegion[3]);
    1962          24 :                 psDGN->max_y = std::max(psDGN->max_y, anRegion[4]);
    1963          24 :                 psDGN->max_z = std::max(psDGN->max_z, anRegion[5]);
    1964             :             }
    1965             :             else
    1966             :             {
    1967          10 :                 psDGN->min_x = anRegion[0];
    1968          10 :                 psDGN->min_y = anRegion[1];
    1969          10 :                 psDGN->min_z = anRegion[2];
    1970          10 :                 psDGN->max_x = anRegion[3];
    1971          10 :                 psDGN->max_y = anRegion[4];
    1972          10 :                 psDGN->max_z = anRegion[5];
    1973          10 :                 psDGN->got_bounds = true;
    1974             :             }
    1975             :         }
    1976             : 
    1977         186 :         psDGN->element_count++;
    1978             : 
    1979         186 :         nLastOffset = VSIFTellL(psDGN->fp);
    1980             :     }
    1981             : 
    1982          45 :     DGNRewind(psDGN);
    1983             : 
    1984          45 :     psDGN->max_element_count = nMaxElements;
    1985             : }

Generated by: LCOV version 1.14