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