Coverage Report

Created: 2026-11-12 08:28

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