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