Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_mapobjectblock.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABMAPObjectBlock class used to handle
7 : * reading/writing of the .MAP files' object data blocks
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999-2001, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 :
17 : #include "cpl_port.h"
18 : #include "mitab.h"
19 :
20 : #include <algorithm>
21 : #include <limits.h>
22 : #include <stddef.h>
23 :
24 : #include "cpl_conv.h"
25 : #include "cpl_error.h"
26 : #include "cpl_vsi.h"
27 : #include "mitab_priv.h"
28 : #include "mitab_utils.h"
29 :
30 : /*=====================================================================
31 : * class TABMAPObjectBlock
32 : *====================================================================*/
33 :
34 : constexpr int MAP_OBJECT_HEADER_SIZE = 20;
35 :
36 : /**********************************************************************
37 : * TABMAPObjectBlock::TABMAPObjectBlock()
38 : *
39 : * Constructor.
40 : **********************************************************************/
41 39330 : TABMAPObjectBlock::TABMAPObjectBlock(TABAccess eAccessMode /*= TABRead*/)
42 : : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0),
43 : m_nFirstCoordBlock(0), m_nLastCoordBlock(0), m_nCenterX(0), m_nCenterY(0),
44 : m_nMinX(0), m_nMinY(0), m_nMaxX(0), m_nMaxY(0), m_nCurObjectOffset(0),
45 39330 : m_nCurObjectId(0), m_nCurObjectType(TAB_GEOM_UNSET), m_bLockCenter(FALSE)
46 : {
47 39330 : }
48 :
49 : /**********************************************************************
50 : * TABMAPObjectBlock::~TABMAPObjectBlock()
51 : *
52 : * Destructor.
53 : **********************************************************************/
54 117990 : TABMAPObjectBlock::~TABMAPObjectBlock()
55 : {
56 : // TODO(schwehr): Why set these? Should remove.
57 39330 : m_nMinX = 1000000000;
58 39330 : m_nMinY = 1000000000;
59 39330 : m_nMaxX = -1000000000;
60 39330 : m_nMaxY = -1000000000;
61 78660 : }
62 :
63 : /**********************************************************************
64 : * TABMAPObjectBlock::InitBlockFromData()
65 : *
66 : * Perform some initialization on the block after its binary data has
67 : * been set or changed (or loaded from a file).
68 : *
69 : * Returns 0 if successful or -1 if an error happened, in which case
70 : * CPLError() will have been called.
71 : **********************************************************************/
72 47346 : int TABMAPObjectBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
73 : int nSizeUsed,
74 : GBool bMakeCopy /* = TRUE */,
75 : VSILFILE *fpSrc /* = NULL */,
76 : int nOffset /* = 0 */)
77 : {
78 : /*-----------------------------------------------------------------
79 : * First of all, we must call the base class' InitBlockFromData()
80 : *----------------------------------------------------------------*/
81 47346 : const int nStatus = TABRawBinBlock::InitBlockFromData(
82 : pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
83 47346 : if (nStatus != 0)
84 0 : return nStatus;
85 :
86 : /*-----------------------------------------------------------------
87 : * Validate block type
88 : *----------------------------------------------------------------*/
89 47346 : if (m_nBlockType != TABMAP_OBJECT_BLOCK)
90 : {
91 0 : CPLError(CE_Failure, CPLE_FileIO,
92 : "InitBlockFromData(): Invalid Block Type: got %d expected %d",
93 : m_nBlockType, TABMAP_OBJECT_BLOCK);
94 0 : CPLFree(m_pabyBuf);
95 0 : m_pabyBuf = nullptr;
96 0 : return -1;
97 : }
98 :
99 : /*-----------------------------------------------------------------
100 : * Init member variables
101 : *----------------------------------------------------------------*/
102 47346 : GotoByteInBlock(0x002);
103 47346 : m_numDataBytes = ReadInt16(); /* Excluding 4 bytes header */
104 47346 : if (m_numDataBytes < 0 ||
105 47346 : m_numDataBytes + MAP_OBJECT_HEADER_SIZE > nBlockSize)
106 : {
107 0 : CPLError(CE_Failure, CPLE_FileIO,
108 : "TABMAPObjectBlock::InitBlockFromData(): m_numDataBytes=%d "
109 : "incompatible with block size %d",
110 : m_numDataBytes, nBlockSize);
111 0 : CPLFree(m_pabyBuf);
112 0 : m_pabyBuf = nullptr;
113 0 : return -1;
114 : }
115 :
116 47346 : m_nCenterX = ReadInt32();
117 47346 : m_nCenterY = ReadInt32();
118 :
119 47346 : m_nFirstCoordBlock = ReadInt32();
120 47346 : m_nLastCoordBlock = ReadInt32();
121 :
122 47346 : m_nCurObjectOffset = -1;
123 47346 : m_nCurObjectId = -1;
124 47346 : m_nCurObjectType = TAB_GEOM_UNSET;
125 :
126 47346 : m_nMinX = 1000000000;
127 47346 : m_nMinY = 1000000000;
128 47346 : m_nMaxX = -1000000000;
129 47346 : m_nMaxY = -1000000000;
130 47346 : m_bLockCenter = FALSE;
131 :
132 : /*-----------------------------------------------------------------
133 : * Set real value for m_nSizeUsed to allow random update
134 : * (By default TABRawBinBlock thinks all bytes are used)
135 : *----------------------------------------------------------------*/
136 47346 : m_nSizeUsed = m_numDataBytes + MAP_OBJECT_HEADER_SIZE;
137 :
138 47346 : return 0;
139 : }
140 :
141 : /************************************************************************
142 : * ClearObjects()
143 : *
144 : * Cleans existing objects from the block. This method is used when
145 : * compacting a page that has deleted records.
146 : ************************************************************************/
147 270 : void TABMAPObjectBlock::ClearObjects()
148 : {
149 270 : GotoByteInBlock(MAP_OBJECT_HEADER_SIZE);
150 270 : WriteZeros(m_nBlockSize - MAP_OBJECT_HEADER_SIZE);
151 270 : GotoByteInBlock(MAP_OBJECT_HEADER_SIZE);
152 270 : m_nSizeUsed = MAP_OBJECT_HEADER_SIZE;
153 270 : m_bModified = TRUE;
154 270 : }
155 :
156 : /************************************************************************
157 : * LockCenter()
158 : *
159 : * Prevents the m_nCenterX and m_nCenterY to be adjusted by other methods.
160 : * Useful when editing pages that have compressed geometries.
161 : * This is a bit band-aid. Proper support of compressed geometries should
162 : * handle center moves.
163 : ************************************************************************/
164 11816 : void TABMAPObjectBlock::LockCenter()
165 : {
166 11816 : m_bLockCenter = TRUE;
167 11816 : }
168 :
169 : /************************************************************************
170 : * SetCenterFromOtherBlock()
171 : *
172 : * Sets the m_nCenterX and m_nCenterY from the one of another block and
173 : * lock them. See LockCenter() as well.
174 : * Used when splitting a page.
175 : ************************************************************************/
176 254 : void TABMAPObjectBlock::SetCenterFromOtherBlock(
177 : TABMAPObjectBlock *poOtherObjBlock)
178 : {
179 254 : m_nCenterX = poOtherObjBlock->m_nCenterX;
180 254 : m_nCenterY = poOtherObjBlock->m_nCenterY;
181 254 : LockCenter();
182 254 : }
183 :
184 : /************************************************************************/
185 : /* Rewind() */
186 : /************************************************************************/
187 778 : void TABMAPObjectBlock::Rewind()
188 : {
189 778 : m_nCurObjectId = -1;
190 778 : m_nCurObjectOffset = -1;
191 778 : m_nCurObjectType = TAB_GEOM_UNSET;
192 778 : }
193 :
194 : /************************************************************************/
195 : /* AdvanceToNextObject() */
196 : /************************************************************************/
197 :
198 427591 : int TABMAPObjectBlock::AdvanceToNextObject(TABMAPHeaderBlock *poHeader)
199 :
200 : {
201 427591 : if (m_nCurObjectId == -1)
202 : {
203 15819 : m_nCurObjectOffset = 20;
204 : }
205 : else
206 : {
207 411772 : m_nCurObjectOffset += poHeader->GetMapObjectSize(m_nCurObjectType);
208 : }
209 :
210 427591 : if (m_nCurObjectOffset + 5 < m_numDataBytes + 20)
211 : {
212 412272 : GotoByteInBlock(m_nCurObjectOffset);
213 412272 : const GByte byVal = ReadByte();
214 412272 : if (TABMAPFile::IsValidObjType(byVal))
215 : {
216 412272 : m_nCurObjectType = static_cast<TABGeomType>(byVal);
217 : }
218 : else
219 : {
220 0 : CPLError(
221 : CE_Warning,
222 : static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
223 : "Unsupported object type %d (0x%2.2x). Feature will be "
224 : "returned with NONE geometry.",
225 : byVal, byVal);
226 0 : m_nCurObjectType = TAB_GEOM_NONE;
227 : }
228 : }
229 : else
230 : {
231 15319 : m_nCurObjectType = TAB_GEOM_UNSET;
232 : }
233 :
234 427591 : if (m_nCurObjectType <= 0 || m_nCurObjectType >= TAB_GEOM_MAX_TYPE)
235 : {
236 15319 : m_nCurObjectType = TAB_GEOM_UNSET;
237 15319 : m_nCurObjectId = -1;
238 15319 : m_nCurObjectOffset = -1;
239 : }
240 : else
241 : {
242 412272 : m_nCurObjectId = ReadInt32();
243 :
244 : // Is this object marked as deleted? If so, skip it.
245 : // I check both the top bits but I have only seen this occur
246 : // with the second highest bit set (i.e. in usa/states.tab). NFW.
247 :
248 412272 : if ((static_cast<GUInt32>(m_nCurObjectId) & 0xC0000000U) != 0)
249 : {
250 16497 : m_nCurObjectId = AdvanceToNextObject(poHeader);
251 : }
252 : }
253 :
254 427591 : return m_nCurObjectId;
255 : }
256 :
257 : /**********************************************************************
258 : * TABMAPObjectBlock::CommitToFile()
259 : *
260 : * Commit the current state of the binary block to the file to which
261 : * it has been previously attached.
262 : *
263 : * This method makes sure all values are properly set in the map object
264 : * block header and then calls TABRawBinBlock::CommitToFile() to do
265 : * the actual writing to disk.
266 : *
267 : * Returns 0 if successful or -1 if an error happened, in which case
268 : * CPLError() will have been called.
269 : **********************************************************************/
270 17016 : int TABMAPObjectBlock::CommitToFile()
271 : {
272 17016 : if (m_pabyBuf == nullptr)
273 : {
274 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
275 : "TABMAPObjectBlock::CommitToFile(): Block has not been "
276 : "initialized yet!");
277 0 : return -1;
278 : }
279 :
280 : /*-----------------------------------------------------------------
281 : * Nothing to do here if block has not been modified
282 : *----------------------------------------------------------------*/
283 17016 : if (!m_bModified)
284 383 : return 0;
285 :
286 : /*-----------------------------------------------------------------
287 : * Make sure 20 bytes block header is up to date.
288 : *----------------------------------------------------------------*/
289 16633 : GotoByteInBlock(0x000);
290 :
291 16633 : WriteInt16(TABMAP_OBJECT_BLOCK); // Block type code
292 16633 : m_numDataBytes = m_nSizeUsed - MAP_OBJECT_HEADER_SIZE;
293 16633 : CPLAssert(m_numDataBytes >= 0 && m_numDataBytes < 32768);
294 16633 : WriteInt16(static_cast<GInt16>(m_numDataBytes)); // num. bytes used
295 :
296 16633 : WriteInt32(m_nCenterX);
297 16633 : WriteInt32(m_nCenterY);
298 :
299 16633 : WriteInt32(m_nFirstCoordBlock);
300 16633 : WriteInt32(m_nLastCoordBlock);
301 :
302 16633 : int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
303 :
304 : /*-----------------------------------------------------------------
305 : * OK, all object data has already been written in the block.
306 : * Call the base class to write the block to disk.
307 : *----------------------------------------------------------------*/
308 16633 : if (nStatus == 0)
309 : {
310 : #ifdef DEBUG_VERBOSE
311 : CPLDebug("MITAB", "Committing OBJECT block to offset %d",
312 : m_nFileOffset);
313 : #endif
314 16633 : nStatus = TABRawBinBlock::CommitToFile();
315 : }
316 :
317 16633 : return nStatus;
318 : }
319 :
320 : /**********************************************************************
321 : * TABMAPObjectBlock::InitNewBlock()
322 : *
323 : * Initialize a newly created block so that it knows to which file it
324 : * is attached, its block size, etc . and then perform any specific
325 : * initialization for this block type, including writing a default
326 : * block header, etc. and leave the block ready to receive data.
327 : *
328 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
329 : * that puts the block in a stable state without loading any initial
330 : * data in it.
331 : *
332 : * Returns 0 if successful or -1 if an error happened, in which case
333 : * CPLError() will have been called.
334 : **********************************************************************/
335 1982 : int TABMAPObjectBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
336 : int nFileOffset /* = 0*/)
337 : {
338 : /*-----------------------------------------------------------------
339 : * Start with the default initialization
340 : *----------------------------------------------------------------*/
341 1982 : if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
342 0 : return -1;
343 :
344 : /*-----------------------------------------------------------------
345 : * And then set default values for the block header.
346 : *----------------------------------------------------------------*/
347 : // Set block MBR to extreme values to force an update on the first
348 : // UpdateMBR() call.
349 1982 : m_nMinX = 1000000000;
350 1982 : m_nMaxX = -1000000000;
351 1982 : m_nMinY = 1000000000;
352 1982 : m_nMaxY = -1000000000;
353 :
354 : // Reset current object refs
355 1982 : m_nCurObjectId = -1;
356 1982 : m_nCurObjectOffset = -1;
357 1982 : m_nCurObjectType = TAB_GEOM_UNSET;
358 :
359 1982 : m_numDataBytes = 0; /* Data size excluding header */
360 1982 : m_nCenterX = 0;
361 1982 : m_nCenterY = 0;
362 1982 : m_nFirstCoordBlock = 0;
363 1982 : m_nLastCoordBlock = 0;
364 :
365 1982 : if (m_eAccess != TABRead && nFileOffset != 0)
366 : {
367 691 : GotoByteInBlock(0x000);
368 :
369 691 : WriteInt16(TABMAP_OBJECT_BLOCK); // Block type code
370 691 : WriteInt16(0); // num. bytes used, excluding header
371 :
372 : // MBR center here... will be written in CommitToFile()
373 691 : WriteInt32(0);
374 691 : WriteInt32(0);
375 :
376 : // First/last coord block ref... will be written in CommitToFile()
377 691 : WriteInt32(0);
378 691 : WriteInt32(0);
379 : }
380 :
381 1982 : if (CPLGetLastErrorType() == CE_Failure)
382 0 : return -1;
383 :
384 1982 : return 0;
385 : }
386 :
387 : /**********************************************************************
388 : * TABMAPObjectBlock::ReadCoord()
389 : *
390 : * Read the next pair of integer coordinates value from the block, and
391 : * apply the translation relative to to the center of the data block
392 : * if bCompressed=TRUE.
393 : *
394 : * This means that the returned coordinates are always absolute integer
395 : * coordinates, even when the source coords are in compressed form.
396 : *
397 : * Returns 0 if successful or -1 if an error happened, in which case
398 : * CPLError() will have been called.
399 : **********************************************************************/
400 545238 : int TABMAPObjectBlock::ReadIntCoord(GBool bCompressed, GInt32 &nX, GInt32 &nY)
401 : {
402 545238 : if (bCompressed)
403 : {
404 13886 : nX = ReadInt16();
405 13886 : nY = ReadInt16();
406 13886 : TABSaturatedAdd(nX, m_nCenterX);
407 13886 : TABSaturatedAdd(nY, m_nCenterY);
408 : }
409 : else
410 : {
411 531352 : nX = ReadInt32();
412 531352 : nY = ReadInt32();
413 : }
414 :
415 545238 : if (CPLGetLastErrorType() == CE_Failure)
416 0 : return -1;
417 :
418 545238 : return 0;
419 : }
420 :
421 : /**********************************************************************
422 : * TABMAPObjectBlock::WriteIntCoord()
423 : *
424 : * Write a pair of integer coordinates values to the current position in the
425 : * the block. If bCompr=TRUE then the coordinates are written relative to
426 : * the object block center... otherwise they're written as 32 bits int.
427 : *
428 : * This function does not maintain the block's MBR and center... it is
429 : * assumed to have been set before the first call to WriteIntCoord()
430 : *
431 : * Returns 0 if successful or -1 if an error happened, in which case
432 : * CPLError() will have been called.
433 : **********************************************************************/
434 25692 : int TABMAPObjectBlock::WriteIntCoord(GInt32 nX, GInt32 nY,
435 : GBool bCompressed /*=FALSE*/)
436 : {
437 :
438 : /*-----------------------------------------------------------------
439 : * Write coords to the file.
440 : *----------------------------------------------------------------*/
441 26349 : if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0)) ||
442 657 : (bCompressed &&
443 657 : (WriteInt16(static_cast<GInt16>(nX - m_nCenterX)) != 0 ||
444 657 : WriteInt16(static_cast<GInt16>(nY - m_nCenterY)) != 0)))
445 : {
446 0 : return -1;
447 : }
448 :
449 25692 : return 0;
450 : }
451 :
452 : /**********************************************************************
453 : * TABMAPObjectBlock::WriteIntMBRCoord()
454 : *
455 : * Write 2 pairs of integer coordinates values to the current position
456 : * in the block after making sure that min values are smaller than
457 : * max values. Use this function to write MBR coordinates for an object.
458 : *
459 : * If bCompr=TRUE then the coordinates are written relative to
460 : * the object block center... otherwise they're written as 32 bits int.
461 : *
462 : * This function does not maintain the block's MBR and center... it is
463 : * assumed to have been set before the first call to WriteIntCoord()
464 : *
465 : * Returns 0 if successful or -1 if an error happened, in which case
466 : * CPLError() will have been called.
467 : **********************************************************************/
468 4 : int TABMAPObjectBlock::WriteIntMBRCoord(GInt32 nXMin, GInt32 nYMin,
469 : GInt32 nXMax, GInt32 nYMax,
470 : GBool bCompressed /*=FALSE*/)
471 : {
472 4 : if (WriteIntCoord(std::min(nXMin, nXMax), std::min(nYMin, nYMax),
473 8 : bCompressed) != 0 ||
474 4 : WriteIntCoord(std::max(nXMin, nXMax), std::max(nYMin, nYMax),
475 : bCompressed) != 0)
476 : {
477 0 : return -1;
478 : }
479 :
480 4 : return 0;
481 : }
482 :
483 : /**********************************************************************
484 : * TABMAPObjectBlock::UpdateMBR()
485 : *
486 : * Update the block's MBR and center.
487 : *
488 : * Returns 0 if successful or -1 if an error happened, in which case
489 : * CPLError() will have been called.
490 : **********************************************************************/
491 52346 : int TABMAPObjectBlock::UpdateMBR(GInt32 nX, GInt32 nY)
492 : {
493 :
494 52346 : if (nX < m_nMinX)
495 1373 : m_nMinX = nX;
496 52346 : if (nX > m_nMaxX)
497 3755 : m_nMaxX = nX;
498 :
499 52346 : if (nY < m_nMinY)
500 1070 : m_nMinY = nY;
501 52346 : if (nY > m_nMaxY)
502 3400 : m_nMaxY = nY;
503 :
504 52346 : if (!m_bLockCenter)
505 : {
506 6918 : m_nCenterX =
507 6918 : static_cast<int>((static_cast<GIntBig>(m_nMinX) + m_nMaxX) / 2);
508 6918 : m_nCenterY =
509 6918 : static_cast<int>((static_cast<GIntBig>(m_nMinY) + m_nMaxY) / 2);
510 : }
511 :
512 52346 : return 0;
513 : }
514 :
515 : /**********************************************************************
516 : * TABMAPObjectBlock::AddCoordBlockRef()
517 : *
518 : * Update the first/last coord block fields in this object to contain
519 : * the specified block address.
520 : **********************************************************************/
521 771 : void TABMAPObjectBlock::AddCoordBlockRef(GInt32 nNewBlockAddress)
522 : {
523 : /*-----------------------------------------------------------------
524 : * Normally, new blocks are added to the end of the list, except
525 : * the first one which is the beginning and the end of the list at
526 : * the same time.
527 : *----------------------------------------------------------------*/
528 771 : if (m_nFirstCoordBlock == 0)
529 61 : m_nFirstCoordBlock = nNewBlockAddress;
530 :
531 771 : m_nLastCoordBlock = nNewBlockAddress;
532 771 : m_bModified = TRUE;
533 771 : }
534 :
535 : /**********************************************************************
536 : * TABMAPObjectBlock::SetMBR()
537 : *
538 : * Set the MBR for the current block.
539 : **********************************************************************/
540 48474 : void TABMAPObjectBlock::SetMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
541 : GInt32 nYMax)
542 : {
543 48474 : m_nMinX = nXMin;
544 48474 : m_nMinY = nYMin;
545 48474 : m_nMaxX = nXMax;
546 48474 : m_nMaxY = nYMax;
547 :
548 48474 : if (!m_bLockCenter)
549 : {
550 3471 : m_nCenterX =
551 3471 : static_cast<int>((static_cast<GIntBig>(m_nMinX) + m_nMaxX) / 2);
552 3471 : m_nCenterY =
553 3471 : static_cast<int>((static_cast<GIntBig>(m_nMinY) + m_nMaxY) / 2);
554 : }
555 48474 : }
556 :
557 : /**********************************************************************
558 : * TABMAPObjectBlock::GetMBR()
559 : *
560 : * Return the MBR for the current block.
561 : **********************************************************************/
562 66102 : void TABMAPObjectBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
563 : GInt32 &nYMax)
564 : {
565 66102 : nXMin = m_nMinX;
566 66102 : nYMin = m_nMinY;
567 66102 : nXMax = m_nMaxX;
568 66102 : nYMax = m_nMaxY;
569 66102 : }
570 :
571 : /**********************************************************************
572 : * TABMAPObjectBlock::PrepareNewObject()
573 : *
574 : * Prepare this block to receive this new object. We only reserve space for
575 : * it in this call. Actual data will be written only when CommitNewObject()
576 : * is called.
577 : *
578 : * Returns the position at which the new object starts
579 : **********************************************************************/
580 26173 : int TABMAPObjectBlock::PrepareNewObject(TABMAPObjHdr *poObjHdr)
581 : {
582 26173 : int nStartAddress = 0;
583 :
584 : // Nothing to do for NONE objects
585 26173 : if (poObjHdr->m_nType == TAB_GEOM_NONE)
586 : {
587 0 : return 0;
588 : }
589 :
590 : // Maintain MBR of this object block.
591 26173 : UpdateMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY);
592 26173 : UpdateMBR(poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
593 :
594 : /*-----------------------------------------------------------------
595 : * Keep track of object type, ID and start address for use by
596 : * CommitNewObject()
597 : *----------------------------------------------------------------*/
598 26173 : nStartAddress = GetFirstUnusedByteOffset();
599 :
600 : // Backup MBR and bLockCenter as they will be reset by GotoByteInFile()
601 : // that will call InitBlockFromData()
602 : GInt32 nXMin, nYMin, nXMax, nYMax;
603 26173 : GetMBR(nXMin, nYMin, nXMax, nYMax);
604 26173 : int bLockCenter = m_bLockCenter;
605 26173 : GotoByteInFile(nStartAddress);
606 26173 : m_bLockCenter = bLockCenter;
607 26173 : SetMBR(nXMin, nYMin, nXMax, nYMax);
608 26173 : m_nCurObjectOffset = nStartAddress - GetStartAddress();
609 :
610 26173 : m_nCurObjectType = poObjHdr->m_nType;
611 26173 : m_nCurObjectId = poObjHdr->m_nId;
612 :
613 26173 : return nStartAddress;
614 : }
615 :
616 : /**********************************************************************
617 : * TABMAPObjectBlock::CommitCurObjData()
618 : *
619 : * Write the ObjHdr to this block. This is usually called after
620 : * PrepareNewObject() once all members of the ObjHdr have
621 : * been set.
622 : *
623 : * Returns 0 if successful or -1 if an error happened, in which case
624 : * CPLError() will have been called.
625 : **********************************************************************/
626 26173 : int TABMAPObjectBlock::CommitNewObject(TABMAPObjHdr *poObjHdr)
627 : {
628 26173 : int nStatus = 0;
629 :
630 26173 : CPLAssert(poObjHdr->m_nType != TAB_GEOM_NONE);
631 :
632 : // Nothing to do for NONE objects
633 26173 : if (poObjHdr->m_nType == TAB_GEOM_NONE)
634 : {
635 0 : return 0;
636 : }
637 :
638 26173 : CPLAssert(m_nCurObjectId == poObjHdr->m_nId);
639 26173 : GotoByteInBlock(m_nCurObjectOffset);
640 :
641 26173 : nStatus = poObjHdr->WriteObj(this);
642 :
643 26173 : if (nStatus == 0)
644 26173 : m_numDataBytes = m_nSizeUsed - MAP_OBJECT_HEADER_SIZE;
645 :
646 26173 : return nStatus;
647 : }
648 :
649 : /**********************************************************************
650 : * TABMAPObjectBlock::Dump()
651 : *
652 : * Dump block contents... available only in DEBUG mode.
653 : **********************************************************************/
654 : #ifdef DEBUG
655 :
656 0 : void TABMAPObjectBlock::Dump(FILE *fpOut, GBool bDetails)
657 : {
658 0 : CPLErrorReset();
659 :
660 0 : if (fpOut == nullptr)
661 0 : fpOut = stdout;
662 :
663 0 : fprintf(fpOut, "----- TABMAPObjectBlock::Dump() -----\n");
664 0 : if (m_pabyBuf == nullptr)
665 : {
666 0 : fprintf(fpOut, "Block has not been initialized yet.");
667 : }
668 : else
669 : {
670 0 : fprintf(fpOut, "Object Data Block (type %d) at offset %d.\n",
671 : m_nBlockType, m_nFileOffset);
672 0 : fprintf(fpOut, " m_numDataBytes = %d\n", m_numDataBytes);
673 0 : fprintf(fpOut, " m_nCenterX = %d\n", m_nCenterX);
674 0 : fprintf(fpOut, " m_nCenterY = %d\n", m_nCenterY);
675 0 : fprintf(fpOut, " m_nFirstCoordBlock = %d\n", m_nFirstCoordBlock);
676 0 : fprintf(fpOut, " m_nLastCoordBlock = %d\n", m_nLastCoordBlock);
677 : }
678 :
679 0 : if (bDetails)
680 : {
681 : /* We need the mapfile's header block */
682 : TABRawBinBlock *poBlock =
683 0 : TABCreateMAPBlockFromFile(m_fp, 0, m_nBlockSize);
684 0 : if (poBlock == nullptr ||
685 0 : poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK)
686 : {
687 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
688 : "Failed reading header block.");
689 0 : return;
690 : }
691 : TABMAPHeaderBlock *poHeader =
692 0 : cpl::down_cast<TABMAPHeaderBlock *>(poBlock);
693 :
694 0 : Rewind();
695 0 : TABMAPObjHdr *poObjHdr = nullptr;
696 0 : while ((poObjHdr = TABMAPObjHdr::ReadNextObj(this, poHeader)) !=
697 : nullptr)
698 : {
699 0 : fprintf(fpOut,
700 : " object id=%d, type=%d, offset=%d (%d), size=%d\n"
701 : " MBR=(%d, %d, %d, %d)\n",
702 0 : m_nCurObjectId, m_nCurObjectType, m_nCurObjectOffset,
703 0 : m_nFileOffset + m_nCurObjectOffset,
704 0 : poHeader->GetMapObjectSize(m_nCurObjectType),
705 : poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
706 : poObjHdr->m_nMaxY);
707 0 : delete poObjHdr;
708 : }
709 :
710 0 : delete poHeader;
711 : }
712 :
713 0 : fflush(fpOut);
714 : }
715 :
716 : #endif // DEBUG
717 :
718 : /*=====================================================================
719 : * class TABMAPObjHdr and family
720 : *====================================================================*/
721 :
722 : /**********************************************************************
723 : * class TABMAPObjHdr
724 : *
725 : * Virtual base class... contains static methods used to allocate instance
726 : * of the derived classes.
727 : *
728 : **********************************************************************/
729 :
730 : TABMAPObjHdr::~TABMAPObjHdr() = default;
731 :
732 : /**********************************************************************
733 : * TABMAPObjHdr::NewObj()
734 : *
735 : * Alloc a new object of specified type or NULL for NONE types or if type
736 : * is not supported.
737 : **********************************************************************/
738 562623 : TABMAPObjHdr *TABMAPObjHdr::NewObj(TABGeomType nNewObjType, GInt32 nId /*=0*/)
739 : {
740 562623 : TABMAPObjHdr *poObj = nullptr;
741 :
742 562623 : switch (nNewObjType)
743 : {
744 215 : case TAB_GEOM_NONE:
745 215 : poObj = new TABMAPObjNone;
746 215 : break;
747 559791 : case TAB_GEOM_SYMBOL_C:
748 : case TAB_GEOM_SYMBOL:
749 559791 : poObj = new TABMAPObjPoint;
750 559791 : break;
751 8 : case TAB_GEOM_FONTSYMBOL_C:
752 : case TAB_GEOM_FONTSYMBOL:
753 8 : poObj = new TABMAPObjFontPoint;
754 8 : break;
755 8 : case TAB_GEOM_CUSTOMSYMBOL_C:
756 : case TAB_GEOM_CUSTOMSYMBOL:
757 8 : poObj = new TABMAPObjCustomPoint;
758 8 : break;
759 44 : case TAB_GEOM_LINE_C:
760 : case TAB_GEOM_LINE:
761 44 : poObj = new TABMAPObjLine;
762 44 : break;
763 2517 : case TAB_GEOM_PLINE_C:
764 : case TAB_GEOM_PLINE:
765 : case TAB_GEOM_REGION_C:
766 : case TAB_GEOM_REGION:
767 : case TAB_GEOM_MULTIPLINE_C:
768 : case TAB_GEOM_MULTIPLINE:
769 : case TAB_GEOM_V450_REGION_C:
770 : case TAB_GEOM_V450_REGION:
771 : case TAB_GEOM_V450_MULTIPLINE_C:
772 : case TAB_GEOM_V450_MULTIPLINE:
773 : case TAB_GEOM_V800_REGION_C:
774 : case TAB_GEOM_V800_REGION:
775 : case TAB_GEOM_V800_MULTIPLINE_C:
776 : case TAB_GEOM_V800_MULTIPLINE:
777 2517 : poObj = new TABMAPObjPLine;
778 2517 : break;
779 8 : case TAB_GEOM_ARC_C:
780 : case TAB_GEOM_ARC:
781 8 : poObj = new TABMAPObjArc;
782 8 : break;
783 12 : case TAB_GEOM_RECT_C:
784 : case TAB_GEOM_RECT:
785 : case TAB_GEOM_ROUNDRECT_C:
786 : case TAB_GEOM_ROUNDRECT:
787 : case TAB_GEOM_ELLIPSE_C:
788 : case TAB_GEOM_ELLIPSE:
789 12 : poObj = new TABMAPObjRectEllipse;
790 12 : break;
791 12 : case TAB_GEOM_TEXT_C:
792 : case TAB_GEOM_TEXT:
793 12 : poObj = new TABMAPObjText;
794 12 : break;
795 4 : case TAB_GEOM_MULTIPOINT_C:
796 : case TAB_GEOM_MULTIPOINT:
797 : case TAB_GEOM_V800_MULTIPOINT_C:
798 : case TAB_GEOM_V800_MULTIPOINT:
799 4 : poObj = new TABMAPObjMultiPoint;
800 4 : break;
801 4 : case TAB_GEOM_COLLECTION_C:
802 : case TAB_GEOM_COLLECTION:
803 : case TAB_GEOM_V800_COLLECTION_C:
804 : case TAB_GEOM_V800_COLLECTION:
805 4 : poObj = new TABMAPObjCollection();
806 4 : break;
807 0 : default:
808 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
809 : "TABMAPObjHdr::NewObj(): Unsupported object type %d",
810 : nNewObjType);
811 : }
812 :
813 562623 : if (poObj)
814 : {
815 562623 : poObj->m_nType = nNewObjType;
816 562623 : poObj->m_nId = nId;
817 562623 : poObj->m_nMinX = poObj->m_nMinY = poObj->m_nMaxX = poObj->m_nMaxY = 0;
818 : }
819 :
820 562623 : return poObj;
821 : }
822 :
823 : /**********************************************************************
824 : * TABMAPObjHdr::ReadNextObj()
825 : *
826 : * Read next object in this block and allocate/init a new object for it
827 : * if successful.
828 : * Returns NULL in case of error or if we reached end of block.
829 : **********************************************************************/
830 20608 : TABMAPObjHdr *TABMAPObjHdr::ReadNextObj(TABMAPObjectBlock *poObjBlock,
831 : TABMAPHeaderBlock *poHeader)
832 : {
833 20608 : TABMAPObjHdr *poObjHdr = nullptr;
834 :
835 20608 : if (poObjBlock->AdvanceToNextObject(poHeader) != -1)
836 : {
837 19830 : poObjHdr = TABMAPObjHdr::NewObj(poObjBlock->GetCurObjectType());
838 39660 : if (poObjHdr &&
839 19830 : ((poObjHdr->m_nId = poObjBlock->GetCurObjectId()) == -1 ||
840 19830 : poObjHdr->ReadObj(poObjBlock) != 0))
841 : {
842 : // Failed reading object in block... an error was already produced
843 0 : delete poObjHdr;
844 0 : return nullptr;
845 : }
846 : }
847 :
848 20608 : return poObjHdr;
849 : }
850 :
851 : /**********************************************************************
852 : * TABMAPObjHdr::IsCompressedType()
853 : *
854 : * Returns TRUE if the current object type uses compressed coordinates
855 : * or FALSE otherwise.
856 : **********************************************************************/
857 578945 : GBool TABMAPObjHdr::IsCompressedType()
858 : {
859 : // Compressed types are 1, 4, 7, etc.
860 578945 : return (m_nType % 3) == 1 ? TRUE : FALSE;
861 : }
862 :
863 : /**********************************************************************
864 : * TABMAPObjHdr::WriteObjTypeAndId()
865 : *
866 : * Writetype+object id information... should be called only by the derived
867 : * classes' WriteObj() methods.
868 : *
869 : * Returns 0 on success, -1 on error.
870 : **********************************************************************/
871 26173 : int TABMAPObjHdr::WriteObjTypeAndId(TABMAPObjectBlock *poObjBlock)
872 : {
873 26173 : poObjBlock->WriteByte(static_cast<GByte>(m_nType));
874 26173 : return poObjBlock->WriteInt32(m_nId);
875 : }
876 :
877 : /**********************************************************************
878 : * TABMAPObjHdr::SetMBR()
879 : *
880 : **********************************************************************/
881 560340 : void TABMAPObjHdr::SetMBR(GInt32 nMinX, GInt32 nMinY, GInt32 nMaxX,
882 : GInt32 nMaxY)
883 : {
884 560340 : m_nMinX = std::min(nMinX, nMaxX);
885 560340 : m_nMinY = std::min(nMinY, nMaxY);
886 560340 : m_nMaxX = std::max(nMinX, nMaxX);
887 560340 : m_nMaxY = std::max(nMinY, nMaxY);
888 560340 : }
889 :
890 : /**********************************************************************
891 : * class TABMAPObjNone
892 : **********************************************************************/
893 :
894 155 : int TABMAPObjNone::ReadObj(TABMAPObjectBlock *)
895 : {
896 155 : return 0;
897 : }
898 :
899 : /**********************************************************************
900 : * class TABMAPObjHdrWithCoord
901 : **********************************************************************/
902 :
903 : TABMAPObjHdrWithCoord::~TABMAPObjHdrWithCoord() = default;
904 :
905 : /**********************************************************************
906 : * class TABMAPObjLine
907 : *
908 : * Applies to 2-points LINEs only
909 : **********************************************************************/
910 :
911 : /**********************************************************************
912 : * TABMAPObjLine::ReadObj()
913 : *
914 : * Read Object information starting after the object id which should
915 : * have been read by TABMAPObjHdr::ReadNextObj() already.
916 : * This function should be called only by TABMAPObjHdr::ReadNextObj().
917 : *
918 : * Returns 0 on success, -1 on error.
919 : **********************************************************************/
920 21 : int TABMAPObjLine::ReadObj(TABMAPObjectBlock *poObjBlock)
921 : {
922 21 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nX1, m_nY1);
923 21 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nX2, m_nY2);
924 :
925 21 : m_nPenId = poObjBlock->ReadByte(); // Pen index
926 :
927 21 : SetMBR(m_nX1, m_nY1, m_nX2, m_nY2);
928 :
929 21 : if (CPLGetLastErrorType() == CE_Failure)
930 0 : return -1;
931 :
932 21 : return 0;
933 : }
934 :
935 : /**********************************************************************
936 : * TABMAPObjLine::WriteObj()
937 : *
938 : * Write Object information with the type+object id
939 : *
940 : * Returns 0 on success, -1 on error.
941 : **********************************************************************/
942 23 : int TABMAPObjLine::WriteObj(TABMAPObjectBlock *poObjBlock)
943 : {
944 : // Write object type and id
945 23 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
946 :
947 23 : poObjBlock->WriteIntCoord(m_nX1, m_nY1, IsCompressedType());
948 23 : poObjBlock->WriteIntCoord(m_nX2, m_nY2, IsCompressedType());
949 :
950 23 : poObjBlock->WriteByte(m_nPenId); // Pen index
951 :
952 23 : if (CPLGetLastErrorType() == CE_Failure)
953 0 : return -1;
954 :
955 23 : return 0;
956 : }
957 :
958 : /**********************************************************************
959 : * class TABMAPObjPLine
960 : *
961 : * Applies to PLINE, MULTIPLINE and REGION object types
962 : **********************************************************************/
963 :
964 : /**********************************************************************
965 : * TABMAPObjPLine::ReadObj()
966 : *
967 : * Read Object information starting after the object id which should
968 : * have been read by TABMAPObjHdr::ReadNextObj() already.
969 : * This function should be called only by TABMAPObjHdr::ReadNextObj().
970 : *
971 : * Returns 0 on success, -1 on error.
972 : **********************************************************************/
973 2214 : int TABMAPObjPLine::ReadObj(TABMAPObjectBlock *poObjBlock)
974 : {
975 2214 : m_nCoordBlockPtr = poObjBlock->ReadInt32();
976 2214 : m_nCoordDataSize = poObjBlock->ReadInt32();
977 :
978 2214 : if (m_nCoordDataSize & 0x80000000)
979 : {
980 4 : m_bSmooth = TRUE;
981 4 : m_nCoordDataSize &= 0x7FFFFFFF; // Take smooth flag out of the value
982 : }
983 : else
984 : {
985 2210 : m_bSmooth = FALSE;
986 : }
987 :
988 : #ifdef TABDUMP
989 : printf("TABMAPObjPLine::ReadObj: m_nCoordDataSize = %d @ %d\n", /*ok*/
990 : m_nCoordDataSize, m_nCoordBlockPtr);
991 : #endif
992 :
993 : // Number of line segments applies only to MULTIPLINE/REGION but not PLINE
994 2214 : if (m_nType == TAB_GEOM_PLINE_C || m_nType == TAB_GEOM_PLINE)
995 : {
996 1689 : m_numLineSections = 1;
997 : }
998 525 : else if (m_nType == TAB_GEOM_V800_REGION ||
999 525 : m_nType == TAB_GEOM_V800_REGION_C ||
1000 525 : m_nType == TAB_GEOM_V800_MULTIPLINE ||
1001 525 : m_nType == TAB_GEOM_V800_MULTIPLINE_C)
1002 : {
1003 : /* V800 REGIONS/MULTIPLINES use an int32 */
1004 0 : m_numLineSections = poObjBlock->ReadInt32();
1005 : /* ... followed by 33 unknown bytes */
1006 0 : poObjBlock->ReadInt32();
1007 0 : poObjBlock->ReadInt32();
1008 0 : poObjBlock->ReadInt32();
1009 0 : poObjBlock->ReadInt32();
1010 0 : poObjBlock->ReadInt32();
1011 0 : poObjBlock->ReadInt32();
1012 0 : poObjBlock->ReadInt32();
1013 0 : poObjBlock->ReadInt32();
1014 0 : poObjBlock->ReadByte();
1015 : }
1016 : else
1017 : {
1018 : /* V300 and V450 REGIONS/MULTIPLINES use an int16 */
1019 525 : m_numLineSections = poObjBlock->ReadInt16();
1020 : }
1021 :
1022 2214 : if (m_numLineSections < 0)
1023 : {
1024 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid numLineSections");
1025 0 : return -1;
1026 : }
1027 :
1028 : #ifdef TABDUMP
1029 : printf("PLINE/REGION: id=%d, type=%d, " /*ok*/
1030 : "CoordBlockPtr=%d, CoordDataSize=%d, numLineSect=%d, bSmooth=%d\n",
1031 : m_nId, m_nType, m_nCoordBlockPtr, m_nCoordDataSize,
1032 : m_numLineSections, m_bSmooth);
1033 : #endif
1034 :
1035 2214 : if (IsCompressedType())
1036 : {
1037 : // Region center/label point, relative to compr. coord. origin
1038 : // No it is not relative to the Object block center
1039 2153 : m_nLabelX = poObjBlock->ReadInt16();
1040 2153 : m_nLabelY = poObjBlock->ReadInt16();
1041 :
1042 : // Compressed coordinate origin (present only in compressed case!)
1043 2153 : m_nComprOrgX = poObjBlock->ReadInt32();
1044 2153 : m_nComprOrgY = poObjBlock->ReadInt32();
1045 :
1046 2153 : TABSaturatedAdd(m_nLabelX, m_nComprOrgX);
1047 2153 : TABSaturatedAdd(m_nLabelY, m_nComprOrgY);
1048 :
1049 2153 : m_nMinX = poObjBlock->ReadInt16(); // Read MBR
1050 2153 : m_nMinY = poObjBlock->ReadInt16();
1051 2153 : m_nMaxX = poObjBlock->ReadInt16();
1052 2153 : m_nMaxY = poObjBlock->ReadInt16();
1053 2153 : TABSaturatedAdd(m_nMinX, m_nComprOrgX);
1054 2153 : TABSaturatedAdd(m_nMinY, m_nComprOrgY);
1055 2153 : TABSaturatedAdd(m_nMaxX, m_nComprOrgX);
1056 2153 : TABSaturatedAdd(m_nMaxY, m_nComprOrgY);
1057 : }
1058 : else
1059 : {
1060 : // Region center/label point, relative to compr. coord. origin
1061 : // No it is not relative to the Object block center
1062 61 : m_nLabelX = poObjBlock->ReadInt32();
1063 61 : m_nLabelY = poObjBlock->ReadInt32();
1064 :
1065 61 : m_nMinX = poObjBlock->ReadInt32(); // Read MBR
1066 61 : m_nMinY = poObjBlock->ReadInt32();
1067 61 : m_nMaxX = poObjBlock->ReadInt32();
1068 61 : m_nMaxY = poObjBlock->ReadInt32();
1069 : }
1070 :
1071 2214 : if (!IsCompressedType())
1072 : {
1073 : // Init. Compr. Origin to a default value in case type is ever changed
1074 61 : m_nComprOrgX =
1075 61 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinX) + m_nMaxX) / 2);
1076 61 : m_nComprOrgY =
1077 61 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinY) + m_nMaxY) / 2);
1078 : }
1079 :
1080 2214 : m_nPenId = poObjBlock->ReadByte(); // Pen index
1081 :
1082 2214 : if (m_nType == TAB_GEOM_REGION || m_nType == TAB_GEOM_REGION_C ||
1083 1695 : m_nType == TAB_GEOM_V450_REGION || m_nType == TAB_GEOM_V450_REGION_C ||
1084 1695 : m_nType == TAB_GEOM_V800_REGION || m_nType == TAB_GEOM_V800_REGION_C)
1085 : {
1086 519 : m_nBrushId = poObjBlock->ReadByte(); // Brush index... REGION only
1087 : }
1088 : else
1089 : {
1090 1695 : m_nBrushId = 0;
1091 : }
1092 :
1093 2214 : if (CPLGetLastErrorType() == CE_Failure)
1094 0 : return -1;
1095 :
1096 2214 : return 0;
1097 : }
1098 :
1099 : /**********************************************************************
1100 : * TABMAPObjPLine::WriteObj()
1101 : *
1102 : * Write Object information with the type+object id
1103 : *
1104 : * Returns 0 on success, -1 on error.
1105 : **********************************************************************/
1106 512 : int TABMAPObjPLine::WriteObj(TABMAPObjectBlock *poObjBlock)
1107 : {
1108 : // Write object type and id
1109 512 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1110 :
1111 512 : poObjBlock->WriteInt32(m_nCoordBlockPtr);
1112 :
1113 : // Combine smooth flag in the coord data size.
1114 512 : if (m_bSmooth)
1115 0 : poObjBlock->WriteInt32(m_nCoordDataSize | 0x80000000);
1116 : else
1117 512 : poObjBlock->WriteInt32(m_nCoordDataSize);
1118 :
1119 : // Number of line segments applies only to MULTIPLINE/REGION but not PLINE
1120 512 : if (m_nType == TAB_GEOM_V800_REGION || m_nType == TAB_GEOM_V800_REGION_C ||
1121 512 : m_nType == TAB_GEOM_V800_MULTIPLINE ||
1122 512 : m_nType == TAB_GEOM_V800_MULTIPLINE_C)
1123 : {
1124 : /* V800 REGIONS/MULTIPLINES use an int32 */
1125 0 : poObjBlock->WriteInt32(m_numLineSections);
1126 : /* ... followed by 33 unknown bytes */
1127 0 : poObjBlock->WriteZeros(33);
1128 : }
1129 512 : else if (m_nType != TAB_GEOM_PLINE_C && m_nType != TAB_GEOM_PLINE)
1130 : {
1131 : /* V300 and V450 REGIONS/MULTIPLINES use an int16 */
1132 105 : poObjBlock->WriteInt16(static_cast<GInt16>(m_numLineSections));
1133 : }
1134 :
1135 512 : if (IsCompressedType())
1136 : {
1137 : // Region center/label point, relative to compr. coord. origin
1138 : // No it is not relative to the Object block center
1139 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nLabelX, m_nComprOrgX));
1140 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nLabelY, m_nComprOrgY));
1141 :
1142 : // Compressed coordinate origin (present only in compressed case!)
1143 502 : poObjBlock->WriteInt32(m_nComprOrgX);
1144 502 : poObjBlock->WriteInt32(m_nComprOrgY);
1145 : }
1146 : else
1147 : {
1148 : // Region center/label point
1149 10 : poObjBlock->WriteInt32(m_nLabelX);
1150 10 : poObjBlock->WriteInt32(m_nLabelY);
1151 : }
1152 :
1153 : // MBR
1154 512 : if (IsCompressedType())
1155 : {
1156 : // MBR relative to PLINE origin (and not object block center)
1157 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinX, m_nComprOrgX));
1158 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinY, m_nComprOrgY));
1159 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxX, m_nComprOrgX));
1160 502 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxY, m_nComprOrgY));
1161 : }
1162 : else
1163 : {
1164 10 : poObjBlock->WriteInt32(m_nMinX);
1165 10 : poObjBlock->WriteInt32(m_nMinY);
1166 10 : poObjBlock->WriteInt32(m_nMaxX);
1167 10 : poObjBlock->WriteInt32(m_nMaxY);
1168 : }
1169 :
1170 512 : poObjBlock->WriteByte(m_nPenId); // Pen index
1171 :
1172 512 : if (m_nType == TAB_GEOM_REGION || m_nType == TAB_GEOM_REGION_C ||
1173 415 : m_nType == TAB_GEOM_V450_REGION || m_nType == TAB_GEOM_V450_REGION_C ||
1174 415 : m_nType == TAB_GEOM_V800_REGION || m_nType == TAB_GEOM_V800_REGION_C)
1175 : {
1176 97 : poObjBlock->WriteByte(m_nBrushId); // Brush index... REGION only
1177 : }
1178 :
1179 512 : if (CPLGetLastErrorType() == CE_Failure)
1180 0 : return -1;
1181 :
1182 512 : return 0;
1183 : }
1184 :
1185 : /**********************************************************************
1186 : * class TABMAPObjPoint
1187 : *
1188 : **********************************************************************/
1189 :
1190 : /**********************************************************************
1191 : * TABMAPObjPoint::ReadObj()
1192 : *
1193 : * Read Object information starting after the object id
1194 : **********************************************************************/
1195 545104 : int TABMAPObjPoint::ReadObj(TABMAPObjectBlock *poObjBlock)
1196 : {
1197 545104 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY);
1198 :
1199 545104 : m_nSymbolId = poObjBlock->ReadByte(); // Symbol index
1200 :
1201 545104 : SetMBR(m_nX, m_nY, m_nX, m_nY);
1202 :
1203 545104 : if (CPLGetLastErrorType() == CE_Failure)
1204 0 : return -1;
1205 :
1206 545104 : return 0;
1207 : }
1208 :
1209 : /**********************************************************************
1210 : * TABMAPObjPoint::WriteObj()
1211 : *
1212 : * Write Object information with the type+object id
1213 : *
1214 : * Returns 0 on success, -1 on error.
1215 : **********************************************************************/
1216 25630 : int TABMAPObjPoint::WriteObj(TABMAPObjectBlock *poObjBlock)
1217 : {
1218 : // Write object type and id
1219 25630 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1220 :
1221 25630 : poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType());
1222 :
1223 25630 : poObjBlock->WriteByte(m_nSymbolId); // Symbol index
1224 :
1225 25630 : if (CPLGetLastErrorType() == CE_Failure)
1226 0 : return -1;
1227 :
1228 25630 : return 0;
1229 : }
1230 :
1231 : /**********************************************************************
1232 : * class TABMAPObjFontPoint
1233 : *
1234 : **********************************************************************/
1235 :
1236 : /**********************************************************************
1237 : * TABMAPObjFontPoint::ReadObj()
1238 : *
1239 : * Read Object information starting after the object id
1240 : **********************************************************************/
1241 6 : int TABMAPObjFontPoint::ReadObj(TABMAPObjectBlock *poObjBlock)
1242 : {
1243 6 : m_nSymbolId = poObjBlock->ReadByte(); // Symbol index
1244 6 : m_nPointSize = poObjBlock->ReadByte();
1245 6 : m_nFontStyle = poObjBlock->ReadInt16(); // font style
1246 :
1247 6 : m_nR = poObjBlock->ReadByte();
1248 6 : m_nG = poObjBlock->ReadByte();
1249 6 : m_nB = poObjBlock->ReadByte();
1250 :
1251 6 : poObjBlock->ReadByte(); // ??? BG Color ???
1252 6 : poObjBlock->ReadByte(); // ???
1253 6 : poObjBlock->ReadByte(); // ???
1254 :
1255 6 : m_nAngle = poObjBlock->ReadInt16();
1256 :
1257 6 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY);
1258 :
1259 6 : m_nFontId = poObjBlock->ReadByte(); // Font name index
1260 :
1261 6 : SetMBR(m_nX, m_nY, m_nX, m_nY);
1262 :
1263 6 : if (CPLGetLastErrorType() == CE_Failure)
1264 0 : return -1;
1265 :
1266 6 : return 0;
1267 : }
1268 :
1269 : /**********************************************************************
1270 : * TABMAPObjFontPoint::WriteObj()
1271 : *
1272 : * Write Object information with the type+object id
1273 : *
1274 : * Returns 0 on success, -1 on error.
1275 : **********************************************************************/
1276 2 : int TABMAPObjFontPoint::WriteObj(TABMAPObjectBlock *poObjBlock)
1277 : {
1278 : // Write object type and id
1279 2 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1280 :
1281 2 : poObjBlock->WriteByte(m_nSymbolId); // symbol shape
1282 2 : poObjBlock->WriteByte(m_nPointSize);
1283 2 : poObjBlock->WriteInt16(m_nFontStyle); // font style
1284 :
1285 2 : poObjBlock->WriteByte(m_nR);
1286 2 : poObjBlock->WriteByte(m_nG);
1287 2 : poObjBlock->WriteByte(m_nB);
1288 :
1289 2 : poObjBlock->WriteByte(0);
1290 2 : poObjBlock->WriteByte(0);
1291 2 : poObjBlock->WriteByte(0);
1292 :
1293 2 : poObjBlock->WriteInt16(m_nAngle);
1294 :
1295 2 : poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType());
1296 :
1297 2 : poObjBlock->WriteByte(m_nFontId); // Font name index
1298 :
1299 2 : if (CPLGetLastErrorType() == CE_Failure)
1300 0 : return -1;
1301 :
1302 2 : return 0;
1303 : }
1304 :
1305 : /**********************************************************************
1306 : * class TABMAPObjCustomPoint
1307 : *
1308 : **********************************************************************/
1309 :
1310 : /**********************************************************************
1311 : * TABMAPObjCustomPoint::ReadObj()
1312 : *
1313 : * Read Object information starting after the object id
1314 : **********************************************************************/
1315 6 : int TABMAPObjCustomPoint::ReadObj(TABMAPObjectBlock *poObjBlock)
1316 : {
1317 6 : m_nUnknown_ = poObjBlock->ReadByte(); // ???
1318 6 : m_nCustomStyle = poObjBlock->ReadByte(); // 0x01=Show BG, 0x02=Apply Color
1319 :
1320 6 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY);
1321 :
1322 6 : m_nSymbolId = poObjBlock->ReadByte(); // Symbol index
1323 6 : m_nFontId = poObjBlock->ReadByte(); // Font index
1324 :
1325 6 : SetMBR(m_nX, m_nY, m_nX, m_nY);
1326 :
1327 6 : if (CPLGetLastErrorType() == CE_Failure)
1328 0 : return -1;
1329 :
1330 6 : return 0;
1331 : }
1332 :
1333 : /**********************************************************************
1334 : * TABMAPObjCustomPoint::WriteObj()
1335 : *
1336 : * Write Object information with the type+object id
1337 : *
1338 : * Returns 0 on success, -1 on error.
1339 : **********************************************************************/
1340 2 : int TABMAPObjCustomPoint::WriteObj(TABMAPObjectBlock *poObjBlock)
1341 : {
1342 : // Write object type and id
1343 2 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1344 :
1345 2 : poObjBlock->WriteByte(m_nUnknown_); // ???
1346 2 : poObjBlock->WriteByte(m_nCustomStyle); // 0x01=Show BG, 0x02=Apply Color
1347 2 : poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType());
1348 :
1349 2 : poObjBlock->WriteByte(m_nSymbolId); // Symbol index
1350 2 : poObjBlock->WriteByte(m_nFontId); // Font index
1351 :
1352 2 : if (CPLGetLastErrorType() == CE_Failure)
1353 0 : return -1;
1354 :
1355 2 : return 0;
1356 : }
1357 :
1358 : /**********************************************************************
1359 : * class TABMAPObjRectEllipse
1360 : *
1361 : **********************************************************************/
1362 :
1363 : /**********************************************************************
1364 : * TABMAPObjRectEllipse::ReadObj()
1365 : *
1366 : * Read Object information starting after the object id
1367 : **********************************************************************/
1368 12 : int TABMAPObjRectEllipse::ReadObj(TABMAPObjectBlock *poObjBlock)
1369 : {
1370 12 : if (m_nType == TAB_GEOM_ROUNDRECT || m_nType == TAB_GEOM_ROUNDRECT_C)
1371 : {
1372 4 : if (IsCompressedType())
1373 : {
1374 0 : m_nCornerWidth = poObjBlock->ReadInt16();
1375 0 : m_nCornerHeight = poObjBlock->ReadInt16();
1376 : }
1377 : else
1378 : {
1379 4 : m_nCornerWidth = poObjBlock->ReadInt32();
1380 4 : m_nCornerHeight = poObjBlock->ReadInt32();
1381 : }
1382 : }
1383 :
1384 12 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY);
1385 12 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY);
1386 :
1387 12 : m_nPenId = poObjBlock->ReadByte(); // Pen index
1388 12 : m_nBrushId = poObjBlock->ReadByte(); // Brush index
1389 :
1390 12 : if (CPLGetLastErrorType() == CE_Failure)
1391 0 : return -1;
1392 :
1393 12 : return 0;
1394 : }
1395 :
1396 : /**********************************************************************
1397 : * TABMAPObjRectEllipse::WriteObj()
1398 : *
1399 : * Write Object information with the type+object id
1400 : *
1401 : * Returns 0 on success, -1 on error.
1402 : **********************************************************************/
1403 0 : int TABMAPObjRectEllipse::WriteObj(TABMAPObjectBlock *poObjBlock)
1404 : {
1405 : // Write object type and id
1406 0 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1407 :
1408 0 : if (m_nType == TAB_GEOM_ROUNDRECT || m_nType == TAB_GEOM_ROUNDRECT_C)
1409 : {
1410 0 : if (IsCompressedType())
1411 : {
1412 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nCornerWidth));
1413 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nCornerHeight));
1414 : }
1415 : else
1416 : {
1417 0 : poObjBlock->WriteInt32(m_nCornerWidth);
1418 0 : poObjBlock->WriteInt32(m_nCornerHeight);
1419 : }
1420 : }
1421 :
1422 0 : poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
1423 : IsCompressedType());
1424 :
1425 0 : poObjBlock->WriteByte(m_nPenId); // Pen index
1426 0 : poObjBlock->WriteByte(m_nBrushId); // Brush index
1427 :
1428 0 : if (CPLGetLastErrorType() == CE_Failure)
1429 0 : return -1;
1430 :
1431 0 : return 0;
1432 : }
1433 :
1434 : /**********************************************************************
1435 : * class TABMAPObjArc
1436 : *
1437 : **********************************************************************/
1438 :
1439 : /**********************************************************************
1440 : * TABMAPObjArc::ReadObj()
1441 : *
1442 : * Read Object information starting after the object id
1443 : **********************************************************************/
1444 8 : int TABMAPObjArc::ReadObj(TABMAPObjectBlock *poObjBlock)
1445 : {
1446 8 : m_nStartAngle = poObjBlock->ReadInt16();
1447 8 : m_nEndAngle = poObjBlock->ReadInt16();
1448 :
1449 : // An arc is defined by its defining ellipse's MBR:
1450 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nArcEllipseMinX,
1451 8 : m_nArcEllipseMinY);
1452 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nArcEllipseMaxX,
1453 8 : m_nArcEllipseMaxY);
1454 :
1455 : // Read the Arc's actual MBR
1456 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY);
1457 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY);
1458 :
1459 8 : m_nPenId = poObjBlock->ReadByte(); // Pen index
1460 :
1461 8 : if (CPLGetLastErrorType() == CE_Failure)
1462 0 : return -1;
1463 :
1464 8 : return 0;
1465 : }
1466 :
1467 : /**********************************************************************
1468 : * TABMAPObjArc::WriteObj()
1469 : *
1470 : * Write Object information with the type+object id
1471 : *
1472 : * Returns 0 on success, -1 on error.
1473 : **********************************************************************/
1474 0 : int TABMAPObjArc::WriteObj(TABMAPObjectBlock *poObjBlock)
1475 : {
1476 : // Write object type and id
1477 0 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1478 :
1479 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nStartAngle));
1480 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nEndAngle));
1481 :
1482 : // An arc is defined by its defining ellipse's MBR:
1483 0 : poObjBlock->WriteIntMBRCoord(m_nArcEllipseMinX, m_nArcEllipseMinY,
1484 : m_nArcEllipseMaxX, m_nArcEllipseMaxY,
1485 : IsCompressedType());
1486 :
1487 : // Write the Arc's actual MBR
1488 0 : poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
1489 : IsCompressedType());
1490 :
1491 0 : poObjBlock->WriteByte(m_nPenId); // Pen index
1492 :
1493 0 : if (CPLGetLastErrorType() == CE_Failure)
1494 0 : return -1;
1495 :
1496 0 : return 0;
1497 : }
1498 :
1499 : /**********************************************************************
1500 : * class TABMAPObjText
1501 : *
1502 : **********************************************************************/
1503 :
1504 : /**********************************************************************
1505 : * TABMAPObjText::ReadObj()
1506 : *
1507 : * Read Object information starting after the object id
1508 : **********************************************************************/
1509 8 : int TABMAPObjText::ReadObj(TABMAPObjectBlock *poObjBlock)
1510 : {
1511 8 : m_nCoordBlockPtr = poObjBlock->ReadInt32(); // String position
1512 8 : m_nCoordDataSize = poObjBlock->ReadInt16(); // String length
1513 8 : if (m_nCoordDataSize < 0)
1514 : {
1515 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "m_nCoordDataSize < 0");
1516 0 : return -1;
1517 : }
1518 8 : m_nTextAlignment = poObjBlock->ReadInt16(); // just./spacing/arrow
1519 :
1520 8 : m_nAngle = poObjBlock->ReadInt16(); // Tenths of degree
1521 :
1522 8 : m_nFontStyle = poObjBlock->ReadInt16(); // Font style/effect
1523 :
1524 8 : m_nFGColorR = poObjBlock->ReadByte();
1525 8 : m_nFGColorG = poObjBlock->ReadByte();
1526 8 : m_nFGColorB = poObjBlock->ReadByte();
1527 :
1528 8 : m_nBGColorR = poObjBlock->ReadByte();
1529 8 : m_nBGColorG = poObjBlock->ReadByte();
1530 8 : m_nBGColorB = poObjBlock->ReadByte();
1531 :
1532 : // Label line end point
1533 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nLineEndX, m_nLineEndY);
1534 :
1535 : // Text Height
1536 8 : if (IsCompressedType())
1537 0 : m_nHeight = poObjBlock->ReadInt16();
1538 : else
1539 8 : m_nHeight = poObjBlock->ReadInt32();
1540 :
1541 : // Font name
1542 8 : m_nFontId = poObjBlock->ReadByte(); // Font name index
1543 :
1544 : // MBR after rotation
1545 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY);
1546 8 : poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY);
1547 :
1548 8 : m_nPenId = poObjBlock->ReadByte(); // Pen index
1549 :
1550 8 : if (CPLGetLastErrorType() == CE_Failure)
1551 0 : return -1;
1552 :
1553 8 : return 0;
1554 : }
1555 :
1556 : /**********************************************************************
1557 : * TABMAPObjText::WriteObj()
1558 : *
1559 : * Write Object information with the type+object id
1560 : *
1561 : * Returns 0 on success, -1 on error.
1562 : **********************************************************************/
1563 4 : int TABMAPObjText::WriteObj(TABMAPObjectBlock *poObjBlock)
1564 : {
1565 : // Write object type and id
1566 4 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1567 :
1568 4 : poObjBlock->WriteInt32(m_nCoordBlockPtr); // String position
1569 4 : poObjBlock->WriteInt16(
1570 4 : static_cast<GInt16>(m_nCoordDataSize)); // String length
1571 4 : poObjBlock->WriteInt16(
1572 4 : static_cast<GInt16>(m_nTextAlignment)); // just./spacing/arrow
1573 :
1574 4 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nAngle)); // Tenths of degree
1575 :
1576 4 : poObjBlock->WriteInt16(m_nFontStyle); // Font style/effect
1577 :
1578 4 : poObjBlock->WriteByte(m_nFGColorR);
1579 4 : poObjBlock->WriteByte(m_nFGColorG);
1580 4 : poObjBlock->WriteByte(m_nFGColorB);
1581 :
1582 4 : poObjBlock->WriteByte(m_nBGColorR);
1583 4 : poObjBlock->WriteByte(m_nBGColorG);
1584 4 : poObjBlock->WriteByte(m_nBGColorB);
1585 :
1586 : // Label line end point
1587 4 : poObjBlock->WriteIntCoord(m_nLineEndX, m_nLineEndY, IsCompressedType());
1588 :
1589 : // Text Height
1590 4 : if (IsCompressedType())
1591 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nHeight));
1592 : else
1593 4 : poObjBlock->WriteInt32(m_nHeight);
1594 :
1595 : // Font name
1596 4 : poObjBlock->WriteByte(m_nFontId); // Font name index
1597 :
1598 : // MBR after rotation
1599 4 : poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
1600 : IsCompressedType());
1601 :
1602 4 : poObjBlock->WriteByte(m_nPenId); // Pen index
1603 :
1604 4 : if (CPLGetLastErrorType() == CE_Failure)
1605 0 : return -1;
1606 :
1607 4 : return 0;
1608 : }
1609 :
1610 : /**********************************************************************
1611 : * class TABMAPObjMultiPoint
1612 : *
1613 : * Applies to PLINE, MULTIPLINE and REGION object types
1614 : **********************************************************************/
1615 :
1616 : /**********************************************************************
1617 : * TABMAPObjMultiPoint::ReadObj()
1618 : *
1619 : * Read Object information starting after the object id which should
1620 : * have been read by TABMAPObjHdr::ReadNextObj() already.
1621 : * This function should be called only by TABMAPObjHdr::ReadNextObj().
1622 : *
1623 : * Returns 0 on success, -1 on error.
1624 : **********************************************************************/
1625 4 : int TABMAPObjMultiPoint::ReadObj(TABMAPObjectBlock *poObjBlock)
1626 : {
1627 4 : m_nCoordBlockPtr = poObjBlock->ReadInt32();
1628 4 : m_nNumPoints = poObjBlock->ReadInt32();
1629 :
1630 4 : const int nPointSize = (IsCompressedType()) ? 2 * 2 : 2 * 4;
1631 4 : if (m_nNumPoints < 0 || m_nNumPoints > INT_MAX / nPointSize)
1632 : {
1633 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid m_nNumPoints = %d",
1634 : m_nNumPoints);
1635 0 : return -1;
1636 : }
1637 4 : m_nCoordDataSize = m_nNumPoints * nPointSize;
1638 :
1639 : #ifdef TABDUMP
1640 : printf("MULTIPOINT: id=%d, type=%d, " /*ok*/
1641 : "CoordBlockPtr=%d, CoordDataSize=%d, numPoints=%d\n",
1642 : m_nId, m_nType, m_nCoordBlockPtr, m_nCoordDataSize, m_nNumPoints);
1643 : #endif
1644 :
1645 : // ?????
1646 4 : poObjBlock->ReadInt32();
1647 4 : poObjBlock->ReadInt32();
1648 4 : poObjBlock->ReadInt32();
1649 4 : poObjBlock->ReadByte();
1650 4 : poObjBlock->ReadByte();
1651 4 : poObjBlock->ReadByte();
1652 :
1653 4 : if (m_nType == TAB_GEOM_V800_MULTIPOINT ||
1654 4 : m_nType == TAB_GEOM_V800_MULTIPOINT_C)
1655 : {
1656 : /* V800 MULTIPOINTS have another 33 unknown bytes... all zeros */
1657 0 : poObjBlock->ReadInt32();
1658 0 : poObjBlock->ReadInt32();
1659 0 : poObjBlock->ReadInt32();
1660 0 : poObjBlock->ReadInt32();
1661 0 : poObjBlock->ReadInt32();
1662 0 : poObjBlock->ReadInt32();
1663 0 : poObjBlock->ReadInt32();
1664 0 : poObjBlock->ReadInt32();
1665 0 : poObjBlock->ReadByte();
1666 : }
1667 :
1668 4 : m_nSymbolId = poObjBlock->ReadByte();
1669 :
1670 : // ?????
1671 4 : poObjBlock->ReadByte();
1672 :
1673 4 : if (IsCompressedType())
1674 : {
1675 : // Region center/label point, relative to compr. coord. origin
1676 : // No it is not relative to the Object block center
1677 4 : m_nLabelX = poObjBlock->ReadInt16();
1678 4 : m_nLabelY = poObjBlock->ReadInt16();
1679 :
1680 : // Compressed coordinate origin
1681 4 : m_nComprOrgX = poObjBlock->ReadInt32();
1682 4 : m_nComprOrgY = poObjBlock->ReadInt32();
1683 :
1684 4 : TABSaturatedAdd(m_nLabelX, m_nComprOrgX);
1685 4 : TABSaturatedAdd(m_nLabelY, m_nComprOrgY);
1686 :
1687 4 : m_nMinX = poObjBlock->ReadInt16(); // Read MBR
1688 4 : m_nMinY = poObjBlock->ReadInt16();
1689 4 : m_nMaxX = poObjBlock->ReadInt16();
1690 4 : m_nMaxY = poObjBlock->ReadInt16();
1691 4 : TABSaturatedAdd(m_nMinX, m_nComprOrgX);
1692 4 : TABSaturatedAdd(m_nMinY, m_nComprOrgY);
1693 4 : TABSaturatedAdd(m_nMaxX, m_nComprOrgX);
1694 4 : TABSaturatedAdd(m_nMaxY, m_nComprOrgY);
1695 : }
1696 : else
1697 : {
1698 : // Region center/label point
1699 0 : m_nLabelX = poObjBlock->ReadInt32();
1700 0 : m_nLabelY = poObjBlock->ReadInt32();
1701 :
1702 0 : m_nMinX = poObjBlock->ReadInt32(); // Read MBR
1703 0 : m_nMinY = poObjBlock->ReadInt32();
1704 0 : m_nMaxX = poObjBlock->ReadInt32();
1705 0 : m_nMaxY = poObjBlock->ReadInt32();
1706 :
1707 : // Init. Compr. Origin to a default value in case type is ever changed
1708 0 : m_nComprOrgX =
1709 0 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinX) + m_nMaxX) / 2);
1710 0 : m_nComprOrgY =
1711 0 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinY) + m_nMaxY) / 2);
1712 : }
1713 :
1714 4 : if (CPLGetLastErrorType() == CE_Failure)
1715 0 : return -1;
1716 :
1717 4 : return 0;
1718 : }
1719 :
1720 : /**********************************************************************
1721 : * TABMAPObjMultiPoint::WriteObj()
1722 : *
1723 : * Write Object information with the type+object id
1724 : *
1725 : * Returns 0 on success, -1 on error.
1726 : **********************************************************************/
1727 0 : int TABMAPObjMultiPoint::WriteObj(TABMAPObjectBlock *poObjBlock)
1728 : {
1729 : // Write object type and id
1730 0 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
1731 :
1732 0 : poObjBlock->WriteInt32(m_nCoordBlockPtr);
1733 :
1734 : // Number of points
1735 0 : poObjBlock->WriteInt32(m_nNumPoints);
1736 :
1737 : // unknown bytes
1738 0 : poObjBlock->WriteZeros(15);
1739 :
1740 0 : if (m_nType == TAB_GEOM_V800_MULTIPOINT ||
1741 0 : m_nType == TAB_GEOM_V800_MULTIPOINT_C)
1742 : {
1743 : /* V800 MULTIPOINTS have another 33 unknown bytes... all zeros */
1744 0 : poObjBlock->WriteZeros(33);
1745 : }
1746 :
1747 : // Symbol Id
1748 0 : poObjBlock->WriteByte(m_nSymbolId);
1749 :
1750 : // ????
1751 0 : poObjBlock->WriteByte(0);
1752 :
1753 : // MBR
1754 0 : if (IsCompressedType())
1755 : {
1756 : // Region center/label point, relative to compr. coord. origin
1757 : // No it is not relative to the Object block center
1758 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nLabelX, m_nComprOrgX));
1759 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nLabelY, m_nComprOrgY));
1760 :
1761 0 : poObjBlock->WriteInt32(m_nComprOrgX);
1762 0 : poObjBlock->WriteInt32(m_nComprOrgY);
1763 :
1764 : // MBR relative to object origin (and not object block center)
1765 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinX, m_nComprOrgX));
1766 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinY, m_nComprOrgY));
1767 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxX, m_nComprOrgX));
1768 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxY, m_nComprOrgY));
1769 : }
1770 : else
1771 : {
1772 : // Region center/label point
1773 0 : poObjBlock->WriteInt32(m_nLabelX);
1774 0 : poObjBlock->WriteInt32(m_nLabelY);
1775 :
1776 0 : poObjBlock->WriteInt32(m_nMinX);
1777 0 : poObjBlock->WriteInt32(m_nMinY);
1778 0 : poObjBlock->WriteInt32(m_nMaxX);
1779 0 : poObjBlock->WriteInt32(m_nMaxY);
1780 : }
1781 :
1782 0 : if (CPLGetLastErrorType() == CE_Failure)
1783 0 : return -1;
1784 :
1785 0 : return 0;
1786 : }
1787 :
1788 : /**********************************************************************
1789 : * class TABMAPObjCollection
1790 : *
1791 : **********************************************************************/
1792 :
1793 : /**********************************************************************
1794 : * TABMAPObjCollection::ReadObj()
1795 : *
1796 : * Read Object information starting after the object id which should
1797 : * have been read by TABMAPObjHdr::ReadNextObj() already.
1798 : * This function should be called only by TABMAPObjHdr::ReadNextObj().
1799 : *
1800 : * Returns 0 on success, -1 on error.
1801 : **********************************************************************/
1802 4 : int TABMAPObjCollection::ReadObj(TABMAPObjectBlock *poObjBlock)
1803 : {
1804 4 : int SIZE_OF_REGION_PLINE_MINI_HDR = 24, SIZE_OF_MPOINT_MINI_HDR = 24;
1805 4 : int nVersion = TAB_GEOM_GET_VERSION(m_nType);
1806 :
1807 : /* Figure the size of the mini-header that we find for each of the
1808 : * 3 optional components (center x,y and mbr)
1809 : */
1810 4 : if (IsCompressedType())
1811 : {
1812 : /* 6 * int16 */
1813 4 : SIZE_OF_REGION_PLINE_MINI_HDR = SIZE_OF_MPOINT_MINI_HDR = 12;
1814 : }
1815 : else
1816 : {
1817 : /* 6 * int32 */
1818 0 : SIZE_OF_REGION_PLINE_MINI_HDR = SIZE_OF_MPOINT_MINI_HDR = 24;
1819 : }
1820 :
1821 4 : if (nVersion >= 800)
1822 : {
1823 : /* extra 4 bytes for num_segments in Region/Pline mini-headers */
1824 0 : SIZE_OF_REGION_PLINE_MINI_HDR += 4;
1825 : }
1826 :
1827 4 : m_nCoordBlockPtr = poObjBlock->ReadInt32(); // pointer into coord block
1828 4 : m_nNumMultiPoints = poObjBlock->ReadInt32(); // no. points in multi point
1829 4 : m_nRegionDataSize =
1830 4 : poObjBlock->ReadInt32(); // size of region data inc. section hdrs
1831 4 : m_nPolylineDataSize =
1832 4 : poObjBlock->ReadInt32(); // size of multipline data inc. section hdrs
1833 :
1834 4 : if (m_nRegionDataSize < 0)
1835 : {
1836 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid m_nRegionDataSize");
1837 0 : return -1;
1838 : }
1839 :
1840 4 : if (m_nPolylineDataSize < 0)
1841 : {
1842 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid m_nRegionDataSize");
1843 0 : return -1;
1844 : }
1845 :
1846 4 : if (nVersion < 800)
1847 : {
1848 : // Num Region/Pline section headers (int16 in V650)
1849 4 : m_nNumRegSections = poObjBlock->ReadInt16();
1850 4 : m_nNumPLineSections = poObjBlock->ReadInt16();
1851 : }
1852 : else
1853 : {
1854 : // Num Region/Pline section headers (int32 in V800)
1855 0 : m_nNumRegSections = poObjBlock->ReadInt32();
1856 0 : m_nNumPLineSections = poObjBlock->ReadInt32();
1857 : }
1858 :
1859 4 : const int nPointSize = (IsCompressedType()) ? 2 * 2 : 2 * 4;
1860 4 : if (m_nNumMultiPoints < 0 || m_nNumMultiPoints > INT_MAX / nPointSize)
1861 : {
1862 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid m_nNumMultiPoints");
1863 0 : return -1;
1864 : }
1865 :
1866 4 : m_nMPointDataSize = m_nNumMultiPoints * nPointSize;
1867 :
1868 : /* NB. MapInfo counts 2 extra bytes per Region and Pline section header
1869 : * in the RegionDataSize and PolylineDataSize values but those 2 extra
1870 : * bytes are not present in the section hdr (possibly due to an alignment
1871 : * to a 4 byte boundary in memory in MapInfo?). The real data size in
1872 : * the CoordBlock is actually 2 bytes shorter per section header than
1873 : * what is written in RegionDataSize and PolylineDataSize values.
1874 : *
1875 : * We'll adjust the values in memory to be the corrected values.
1876 : */
1877 4 : if (m_nNumRegSections < 0 || m_nNumRegSections > INT_MAX / 2 ||
1878 4 : m_nRegionDataSize < 2 * m_nNumRegSections)
1879 : {
1880 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1881 : "Invalid m_nNumRegSections / m_nRegionDataSize");
1882 0 : return -1;
1883 : }
1884 4 : m_nRegionDataSize = m_nRegionDataSize - (2 * m_nNumRegSections);
1885 :
1886 4 : if (m_nNumPLineSections < 0 || m_nNumPLineSections > INT_MAX / 2 ||
1887 4 : m_nPolylineDataSize < 2 * m_nNumPLineSections)
1888 : {
1889 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1890 : "Invalid m_nNumPLineSections / m_nPolylineDataSize");
1891 0 : return -1;
1892 : }
1893 4 : m_nPolylineDataSize = m_nPolylineDataSize - (2 * m_nNumPLineSections);
1894 :
1895 : /* Compute total coord block data size, required when splitting blocks */
1896 4 : m_nCoordDataSize = 0;
1897 :
1898 4 : if (m_nNumRegSections > 0)
1899 : {
1900 4 : if (m_nRegionDataSize > INT_MAX - SIZE_OF_REGION_PLINE_MINI_HDR ||
1901 4 : m_nCoordDataSize >
1902 4 : INT_MAX - (SIZE_OF_REGION_PLINE_MINI_HDR + m_nRegionDataSize))
1903 : {
1904 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1905 : "Invalid m_nCoordDataSize / m_nRegionDataSize");
1906 0 : return -1;
1907 : }
1908 4 : m_nCoordDataSize += SIZE_OF_REGION_PLINE_MINI_HDR + m_nRegionDataSize;
1909 : }
1910 4 : if (m_nNumPLineSections > 0)
1911 : {
1912 4 : if (m_nPolylineDataSize > INT_MAX - SIZE_OF_REGION_PLINE_MINI_HDR ||
1913 4 : m_nCoordDataSize >
1914 4 : INT_MAX - (SIZE_OF_REGION_PLINE_MINI_HDR + m_nPolylineDataSize))
1915 : {
1916 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1917 : "Invalid m_nCoordDataSize / m_nPolylineDataSize");
1918 0 : return -1;
1919 : }
1920 4 : m_nCoordDataSize += SIZE_OF_REGION_PLINE_MINI_HDR + m_nPolylineDataSize;
1921 : }
1922 4 : if (m_nNumMultiPoints > 0)
1923 : {
1924 4 : if (m_nMPointDataSize > INT_MAX - SIZE_OF_MPOINT_MINI_HDR ||
1925 4 : m_nCoordDataSize >
1926 4 : INT_MAX - (SIZE_OF_MPOINT_MINI_HDR + m_nMPointDataSize))
1927 : {
1928 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1929 : "Invalid m_nCoordDataSize / m_nMPointDataSize");
1930 0 : return -1;
1931 : }
1932 4 : m_nCoordDataSize += SIZE_OF_MPOINT_MINI_HDR + m_nMPointDataSize;
1933 : }
1934 :
1935 : #ifdef TABDUMP
1936 : printf("COLLECTION: id=%d, type=%d (0x%x), " /*ok*/
1937 : "CoordBlockPtr=%d, numRegionSections=%d (size=%d+%d), "
1938 : "numPlineSections=%d (size=%d+%d), numPoints=%d (size=%d+%d)\n",
1939 : m_nId, m_nType, m_nType, m_nCoordBlockPtr, m_nNumRegSections,
1940 : m_nRegionDataSize, SIZE_OF_REGION_PLINE_MINI_HDR,
1941 : m_nNumPLineSections, m_nPolylineDataSize,
1942 : SIZE_OF_REGION_PLINE_MINI_HDR, m_nNumMultiPoints, m_nMPointDataSize,
1943 : SIZE_OF_MPOINT_MINI_HDR);
1944 : #endif
1945 :
1946 4 : if (nVersion >= 800)
1947 : {
1948 : // Extra byte in V800 files... value always 4???
1949 0 : int nValue = poObjBlock->ReadByte();
1950 0 : if (nValue != 4)
1951 : {
1952 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1953 : "TABMAPObjCollection::ReadObj(): Byte 29 in Collection "
1954 : "object header not equal to 4 as expected. Value is %d. "
1955 : "Please report this error to the MITAB list so that "
1956 : "MITAB can be extended to support this case.",
1957 : nValue);
1958 : // We don't return right away, the error should be caught at the
1959 : // end of this function.
1960 : }
1961 : }
1962 :
1963 : // ??? All zeros ???
1964 4 : poObjBlock->ReadInt32();
1965 4 : poObjBlock->ReadInt32();
1966 4 : poObjBlock->ReadInt32();
1967 4 : poObjBlock->ReadByte();
1968 4 : poObjBlock->ReadByte();
1969 4 : poObjBlock->ReadByte();
1970 :
1971 4 : m_nMultiPointSymbolId = poObjBlock->ReadByte();
1972 :
1973 4 : poObjBlock->ReadByte(); // ???
1974 4 : m_nRegionPenId = poObjBlock->ReadByte();
1975 4 : m_nPolylinePenId = poObjBlock->ReadByte();
1976 4 : m_nRegionBrushId = poObjBlock->ReadByte();
1977 :
1978 4 : if (IsCompressedType())
1979 : {
1980 : #ifdef TABDUMP
1981 : printf("COLLECTION: READING ComprOrg @ %d\n", /*ok*/
1982 : poObjBlock->GetCurAddress());
1983 : #endif
1984 : // Compressed coordinate origin
1985 4 : m_nComprOrgX = poObjBlock->ReadInt32();
1986 4 : m_nComprOrgY = poObjBlock->ReadInt32();
1987 :
1988 4 : m_nMinX = poObjBlock->ReadInt16(); // Read MBR
1989 4 : m_nMinY = poObjBlock->ReadInt16();
1990 4 : m_nMaxX = poObjBlock->ReadInt16();
1991 4 : m_nMaxY = poObjBlock->ReadInt16();
1992 4 : TABSaturatedAdd(m_nMinX, m_nComprOrgX);
1993 4 : TABSaturatedAdd(m_nMinY, m_nComprOrgY);
1994 4 : TABSaturatedAdd(m_nMaxX, m_nComprOrgX);
1995 4 : TABSaturatedAdd(m_nMaxY, m_nComprOrgY);
1996 : #ifdef TABDUMP
1997 : printf("COLLECTION: ComprOrgX,Y= (%d,%d)\n", /*ok*/
1998 : m_nComprOrgX, m_nComprOrgY);
1999 : #endif
2000 : }
2001 : else
2002 : {
2003 0 : m_nMinX = poObjBlock->ReadInt32(); // Read MBR
2004 0 : m_nMinY = poObjBlock->ReadInt32();
2005 0 : m_nMaxX = poObjBlock->ReadInt32();
2006 0 : m_nMaxY = poObjBlock->ReadInt32();
2007 :
2008 : // Init. Compr. Origin to a default value in case type is ever changed
2009 0 : m_nComprOrgX =
2010 0 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinX) + m_nMaxX) / 2);
2011 0 : m_nComprOrgY =
2012 0 : static_cast<GInt32>((static_cast<GIntBig>(m_nMinY) + m_nMaxY) / 2);
2013 : }
2014 :
2015 4 : if (CPLGetLastErrorType() == CE_Failure)
2016 0 : return -1;
2017 :
2018 4 : return 0;
2019 : }
2020 :
2021 : /**********************************************************************
2022 : * TABMAPObjCollection::WriteObj()
2023 : *
2024 : * Write Object information with the type+object id
2025 : *
2026 : * Returns 0 on success, -1 on error.
2027 : **********************************************************************/
2028 0 : int TABMAPObjCollection::WriteObj(TABMAPObjectBlock *poObjBlock)
2029 : {
2030 : // Write object type and id
2031 0 : TABMAPObjHdr::WriteObjTypeAndId(poObjBlock);
2032 :
2033 0 : int nVersion = TAB_GEOM_GET_VERSION(m_nType);
2034 :
2035 : /* NB. MapInfo counts 2 extra bytes per Region and Pline section header
2036 : * in the RegionDataSize and PolylineDataSize values but those 2 extra
2037 : * bytes are not present in the section hdr (possibly due to an alignment
2038 : * to a 4 byte boundary in memory in MapInfo?). The real data size in
2039 : * the CoordBlock is actually 2 bytes shorter per section header than
2040 : * what is written in RegionDataSize and PolylineDataSize values.
2041 : *
2042 : * The values in memory are the corrected values so we need to add 2 bytes
2043 : * per section header in the values that we write on disk to emulate
2044 : * MapInfo's behavior.
2045 : */
2046 0 : GInt32 nRegionDataSizeMI = m_nRegionDataSize + (2 * m_nNumRegSections);
2047 0 : GInt32 nPolylineDataSizeMI =
2048 0 : m_nPolylineDataSize + (2 * m_nNumPLineSections);
2049 :
2050 0 : poObjBlock->WriteInt32(m_nCoordBlockPtr); // pointer into coord block
2051 0 : poObjBlock->WriteInt32(m_nNumMultiPoints); // no. points in multi point
2052 0 : poObjBlock->WriteInt32(
2053 : nRegionDataSizeMI); // size of region data inc. section hdrs
2054 0 : poObjBlock->WriteInt32(
2055 : nPolylineDataSizeMI); // size of Mpolyline data inc. section hdrs
2056 :
2057 0 : if (nVersion < 800)
2058 : {
2059 : // Num Region/Pline section headers (int16 in V650)
2060 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nNumRegSections));
2061 0 : poObjBlock->WriteInt16(static_cast<GInt16>(m_nNumPLineSections));
2062 : }
2063 : else
2064 : {
2065 : // Num Region/Pline section headers (int32 in V800)
2066 0 : poObjBlock->WriteInt32(m_nNumRegSections);
2067 0 : poObjBlock->WriteInt32(m_nNumPLineSections);
2068 : }
2069 :
2070 0 : if (nVersion >= 800)
2071 : {
2072 : // Extra byte in V800 files... value always 4???
2073 0 : poObjBlock->WriteByte(4);
2074 : }
2075 :
2076 : // Unknown data ?????
2077 0 : poObjBlock->WriteInt32(0);
2078 0 : poObjBlock->WriteInt32(0);
2079 0 : poObjBlock->WriteInt32(0);
2080 0 : poObjBlock->WriteByte(0);
2081 0 : poObjBlock->WriteByte(0);
2082 0 : poObjBlock->WriteByte(0);
2083 :
2084 0 : poObjBlock->WriteByte(m_nMultiPointSymbolId);
2085 :
2086 0 : poObjBlock->WriteByte(0);
2087 0 : poObjBlock->WriteByte(m_nRegionPenId);
2088 0 : poObjBlock->WriteByte(m_nPolylinePenId);
2089 0 : poObjBlock->WriteByte(m_nRegionBrushId);
2090 :
2091 0 : if (IsCompressedType())
2092 : {
2093 : #ifdef TABDUMP
2094 : printf("COLLECTION: WRITING ComprOrgX,Y= (%d,%d) @ %d\n", /*ok*/
2095 : m_nComprOrgX, m_nComprOrgY, poObjBlock->GetCurAddress());
2096 : #endif
2097 : // Compressed coordinate origin
2098 0 : poObjBlock->WriteInt32(m_nComprOrgX);
2099 0 : poObjBlock->WriteInt32(m_nComprOrgY);
2100 :
2101 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinX, m_nComprOrgX)); // MBR
2102 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMinY, m_nComprOrgY));
2103 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxX, m_nComprOrgX));
2104 0 : poObjBlock->WriteInt16(TABInt16Diff(m_nMaxY, m_nComprOrgY));
2105 : }
2106 : else
2107 : {
2108 0 : poObjBlock->WriteInt32(m_nMinX); // MBR
2109 0 : poObjBlock->WriteInt32(m_nMinY);
2110 0 : poObjBlock->WriteInt32(m_nMaxX);
2111 0 : poObjBlock->WriteInt32(m_nMaxY);
2112 : }
2113 :
2114 0 : if (CPLGetLastErrorType() == CE_Failure)
2115 0 : return -1;
2116 :
2117 0 : return 0;
2118 : }
|