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