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