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