LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_maptoolblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 72 131 55.0 %
Date: 2024-11-21 22:18:42 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_maptoollock.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABMAPToolBlock class used to handle
       7             :  *           reading/writing of the .MAP files' drawing tool blocks
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999, 2000, 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 <cstddef>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_vsi.h"
      25             : #include "mitab_priv.h"
      26             : 
      27             : /*=====================================================================
      28             :  *                      class TABMAPToolBlock
      29             :  *====================================================================*/
      30             : 
      31             : constexpr int MAP_TOOL_HEADER_SIZE = 8;
      32             : 
      33             : /**********************************************************************
      34             :  *                   TABMAPToolBlock::TABMAPToolBlock()
      35             :  *
      36             :  * Constructor.
      37             :  **********************************************************************/
      38        2376 : TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/)
      39             :     : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0), m_nNextToolBlock(0),
      40             :       m_numBlocksInChain(1),  // Current block counts as 1
      41        2376 :       m_poBlockManagerRef(nullptr)
      42             : {
      43        2376 : }
      44             : 
      45             : /**********************************************************************
      46             :  *                   TABMAPToolBlock::~TABMAPToolBlock()
      47             :  *
      48             :  * Destructor.
      49             :  **********************************************************************/
      50        4752 : TABMAPToolBlock::~TABMAPToolBlock()
      51             : {
      52        4752 : }
      53             : 
      54             : /**********************************************************************
      55             :  *                   TABMAPToolBlock::EndOfChain()
      56             :  *
      57             :  * Return TRUE if we reached the end of the last block in the chain
      58             :  * TABMAPToolBlocks, or FALSE if there is still data to be read from
      59             :  * this chain.
      60             :  **********************************************************************/
      61        2495 : GBool TABMAPToolBlock::EndOfChain()
      62             : {
      63        2495 :     if (m_pabyBuf && (m_nCurPos < (m_numDataBytes + MAP_TOOL_HEADER_SIZE) ||
      64        1211 :                       m_nNextToolBlock > 0))
      65             :     {
      66        1284 :         return FALSE;  // There is still data to be read.
      67             :     }
      68             : 
      69        1211 :     return TRUE;
      70             : }
      71             : 
      72             : /**********************************************************************
      73             :  *                   TABMAPToolBlock::InitBlockFromData()
      74             :  *
      75             :  * Perform some initialization on the block after its binary data has
      76             :  * been set or changed (or loaded from a file).
      77             :  *
      78             :  * Returns 0 if successful or -1 if an error happened, in which case
      79             :  * CPLError() will have been called.
      80             :  **********************************************************************/
      81        1211 : int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
      82             :                                        int nSizeUsed,
      83             :                                        GBool bMakeCopy /* = TRUE */,
      84             :                                        VSILFILE *fpSrc /* = NULL */,
      85             :                                        int nOffset /* = 0 */)
      86             : {
      87             :     /*-----------------------------------------------------------------
      88             :      * First of all, we must call the base class' InitBlockFromData()
      89             :      *----------------------------------------------------------------*/
      90        1211 :     const int nStatus = TABRawBinBlock::InitBlockFromData(
      91             :         pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
      92        1211 :     if (nStatus != 0)
      93           0 :         return nStatus;
      94             : 
      95             :     /*-----------------------------------------------------------------
      96             :      * Validate block type
      97             :      *----------------------------------------------------------------*/
      98        1211 :     if (m_nBlockType != TABMAP_TOOL_BLOCK)
      99             :     {
     100           0 :         CPLError(CE_Failure, CPLE_FileIO,
     101             :                  "InitBlockFromData(): Invalid Block Type: got %d expected %d",
     102             :                  m_nBlockType, TABMAP_TOOL_BLOCK);
     103           0 :         CPLFree(m_pabyBuf);
     104           0 :         m_pabyBuf = nullptr;
     105           0 :         return -1;
     106             :     }
     107             : 
     108             :     /*-----------------------------------------------------------------
     109             :      * Init member variables
     110             :      *----------------------------------------------------------------*/
     111        1211 :     GotoByteInBlock(0x002);
     112        1211 :     m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
     113        1211 :     if (m_numDataBytes < 0 ||
     114        1211 :         m_numDataBytes + MAP_TOOL_HEADER_SIZE > nBlockSize)
     115             :     {
     116           0 :         CPLError(CE_Failure, CPLE_FileIO,
     117             :                  "TABMAPToolBlock::InitBlockFromData(): m_numDataBytes=%d "
     118             :                  "incompatible with block size %d",
     119             :                  m_numDataBytes, nBlockSize);
     120           0 :         CPLFree(m_pabyBuf);
     121           0 :         m_pabyBuf = nullptr;
     122           0 :         return -1;
     123             :     }
     124             : 
     125        1211 :     m_nNextToolBlock = ReadInt32();
     126        1211 :     if (m_nNextToolBlock != 0 &&
     127           0 :         (m_nNextToolBlock / m_nBlockSize) * m_nBlockSize == nOffset)
     128             :     {
     129           0 :         CPLError(CE_Failure, CPLE_FileIO,
     130             :                  "InitBlockFromData(): self referencing block");
     131           0 :         CPLFree(m_pabyBuf);
     132           0 :         m_pabyBuf = nullptr;
     133           0 :         return -1;
     134             :     }
     135             : 
     136             :     /*-----------------------------------------------------------------
     137             :      * The read ptr is now located at the beginning of the data part.
     138             :      *----------------------------------------------------------------*/
     139        1211 :     GotoByteInBlock(MAP_TOOL_HEADER_SIZE);
     140             : 
     141        1211 :     return 0;
     142             : }
     143             : 
     144             : /**********************************************************************
     145             :  *                   TABMAPToolBlock::CommitToFile()
     146             :  *
     147             :  * Commit the current state of the binary block to the file to which
     148             :  * it has been previously attached.
     149             :  *
     150             :  * This method makes sure all values are properly set in the map object
     151             :  * block header and then calls TABRawBinBlock::CommitToFile() to do
     152             :  * the actual writing to disk.
     153             :  *
     154             :  * Returns 0 if successful or -1 if an error happened, in which case
     155             :  * CPLError() will have been called.
     156             :  **********************************************************************/
     157        1165 : int TABMAPToolBlock::CommitToFile()
     158             : {
     159        1165 :     if (m_pabyBuf == nullptr)
     160             :     {
     161           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     162             :                  "CommitToFile(): Block has not been initialized yet!");
     163           0 :         return -1;
     164             :     }
     165             : 
     166             :     /*-----------------------------------------------------------------
     167             :      * Nothing to do here if block has not been modified
     168             :      *----------------------------------------------------------------*/
     169        1165 :     if (!m_bModified)
     170           0 :         return 0;
     171             : 
     172             :     /*-----------------------------------------------------------------
     173             :      * Make sure 8 bytes block header is up to date.
     174             :      *----------------------------------------------------------------*/
     175        1165 :     GotoByteInBlock(0x000);
     176             : 
     177        1165 :     WriteInt16(TABMAP_TOOL_BLOCK);  // Block type code
     178        1165 :     CPLAssert(m_nSizeUsed >= MAP_TOOL_HEADER_SIZE &&
     179             :               m_nSizeUsed < MAP_TOOL_HEADER_SIZE + 32768);
     180        1165 :     WriteInt16(static_cast<GInt16>(m_nSizeUsed -
     181             :                                    MAP_TOOL_HEADER_SIZE));  // num. bytes used
     182        1165 :     WriteInt32(m_nNextToolBlock);
     183             : 
     184        1165 :     int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
     185             : 
     186             :     /*-----------------------------------------------------------------
     187             :      * OK, call the base class to write the block to disk.
     188             :      *----------------------------------------------------------------*/
     189        1165 :     if (nStatus == 0)
     190             :     {
     191             : #ifdef DEBUG_VERBOSE
     192             :         CPLDebug("MITAB", "Committing TOOL block to offset %d", m_nFileOffset);
     193             : #endif
     194        1165 :         nStatus = TABRawBinBlock::CommitToFile();
     195             :     }
     196             : 
     197        1165 :     return nStatus;
     198             : }
     199             : 
     200             : /**********************************************************************
     201             :  *                   TABMAPToolBlock::InitNewBlock()
     202             :  *
     203             :  * Initialize a newly created block so that it knows to which file it
     204             :  * is attached, its block size, etc . and then perform any specific
     205             :  * initialization for this block type, including writing a default
     206             :  * block header, etc. and leave the block ready to receive data.
     207             :  *
     208             :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     209             :  * that puts the block in a stable state without loading any initial
     210             :  * data in it.
     211             :  *
     212             :  * Returns 0 if successful or -1 if an error happened, in which case
     213             :  * CPLError() will have been called.
     214             :  **********************************************************************/
     215        2376 : int TABMAPToolBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
     216             :                                   int nFileOffset /* = 0*/)
     217             : {
     218             : #ifdef DEBUG_VERBOSE
     219             :     CPLDebug("MITAB", "Instantiating new TOOL block at offset %d", nFileOffset);
     220             : #endif
     221             : 
     222             :     /*-----------------------------------------------------------------
     223             :      * Start with the default initialization
     224             :      *----------------------------------------------------------------*/
     225        2376 :     if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
     226           0 :         return -1;
     227             : 
     228             :     /*-----------------------------------------------------------------
     229             :      * And then set default values for the block header.
     230             :      *----------------------------------------------------------------*/
     231        2376 :     m_nNextToolBlock = 0;
     232             : 
     233        2376 :     m_numDataBytes = 0;
     234             : 
     235        2376 :     GotoByteInBlock(0x000);
     236             : 
     237        2376 :     if (m_eAccess != TABRead)
     238             :     {
     239        1165 :         WriteInt16(TABMAP_TOOL_BLOCK);  // Block type code
     240        1165 :         WriteInt16(0);                  // num. bytes used, excluding header
     241        1165 :         WriteInt32(0);                  // Pointer to next tool block
     242             :     }
     243             : 
     244        2376 :     if (CPLGetLastErrorType() == CE_Failure)
     245           0 :         return -1;
     246             : 
     247        2376 :     return 0;
     248             : }
     249             : 
     250             : /**********************************************************************
     251             :  *                   TABMAPToolBlock::SetNextToolBlock()
     252             :  *
     253             :  * Set the address (offset from beginning of file) of the drawing tool block
     254             :  * that follows the current one.
     255             :  **********************************************************************/
     256           0 : void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress)
     257             : {
     258           0 :     m_nNextToolBlock = nNextToolBlockAddress;
     259           0 : }
     260             : 
     261             : /**********************************************************************
     262             :  *                   TABMAPToolBlock::SetMAPBlockManagerRef()
     263             :  *
     264             :  * Pass a reference to the block manager object for the file this
     265             :  * block belongs to.  The block manager will be used by this object
     266             :  * when it needs to automatically allocate a new block.
     267             :  **********************************************************************/
     268        1165 : void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
     269             : {
     270        1165 :     m_poBlockManagerRef = poBlockMgr;
     271        1165 : }
     272             : 
     273             : /**********************************************************************
     274             :  *                   TABMAPToolBlock::ReadBytes()
     275             :  *
     276             :  * Cover function for TABRawBinBlock::ReadBytes() that will automagically
     277             :  * load the next coordinate block in the chain before reading the
     278             :  * requested bytes if we are at the end of the current block and if
     279             :  * m_nNextToolBlock is a valid block.
     280             :  *
     281             :  * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
     282             :  * work:
     283             :  * Copy the number of bytes from the data block's internal buffer to
     284             :  * the user's buffer pointed by pabyDstBuf.
     285             :  *
     286             :  * Passing pabyDstBuf = NULL will only move the read pointer by the
     287             :  * specified number of bytes as if the copy had happened... but it
     288             :  * won't crash.
     289             :  *
     290             :  * Returns 0 if successful or -1 if an error happened, in which case
     291             :  * CPLError() will have been called.
     292             :  **********************************************************************/
     293       12693 : int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
     294             : {
     295       12693 :     if (m_pabyBuf && m_nCurPos >= (m_numDataBytes + MAP_TOOL_HEADER_SIZE) &&
     296           0 :         m_nNextToolBlock > 0)
     297             :     {
     298           0 :         int nStatus = GotoByteInFile(m_nNextToolBlock);
     299           0 :         if (nStatus != 0)
     300             :         {
     301             :             // Failed.... an error has already been reported.
     302           0 :             return nStatus;
     303             :         }
     304             : 
     305           0 :         GotoByteInBlock(MAP_TOOL_HEADER_SIZE);  // Move pointer past header
     306             : 
     307             :         // FIXME ? what about a corrupted tab file that would have more than
     308             :         // 255 blocks
     309           0 :         m_numBlocksInChain++;
     310             :     }
     311             : 
     312       12693 :     return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
     313             : }
     314             : 
     315             : /**********************************************************************
     316             :  *                   TABMAPToolBlock::WriteBytes()
     317             :  *
     318             :  * Cover function for TABRawBinBlock::WriteBytes() that will automagically
     319             :  * CommitToFile() the current block and create a new one if we are at
     320             :  * the end of the current block.
     321             :  *
     322             :  * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
     323             :  * work.
     324             :  *
     325             :  * Passing pabySrcBuf = NULL will only move the write pointer by the
     326             :  * specified number of bytes as if the copy had happened... but it
     327             :  * won't crash.
     328             :  *
     329             :  * Returns 0 if successful or -1 if an error happened, in which case
     330             :  * CPLError() will have been called.
     331             :  **********************************************************************/
     332       16788 : int TABMAPToolBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
     333             : {
     334       16788 :     if (m_eAccess == TABWrite && m_poBlockManagerRef &&
     335        1723 :         (m_nBlockSize - m_nCurPos) < nBytesToWrite)
     336             :     {
     337           0 :         if (m_numBlocksInChain >= 255)
     338             :         {
     339           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     340             :                      "Maximum number of 255 tool blocks reached");
     341           0 :             return -1;
     342             :         }
     343             : 
     344           0 :         int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
     345           0 :         SetNextToolBlock(nNewBlockOffset);
     346             : 
     347           0 :         if (CommitToFile() != 0 ||
     348           0 :             InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
     349             :         {
     350             :             // An error message should have already been reported.
     351           0 :             return -1;
     352             :         }
     353             : 
     354           0 :         m_numBlocksInChain++;
     355             :     }
     356             : 
     357       16788 :     return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
     358             : }
     359             : 
     360             : /**********************************************************************
     361             :  *                   TABMAPToolBlock::CheckAvailableSpace()
     362             :  *
     363             :  * Check if an object of the specified type can fit in
     364             :  * current block.  If it can't fit then force committing current block
     365             :  * and allocating a new one.
     366             :  *
     367             :  * Returns 0 if successful or -1 if an error happened, in which case
     368             :  * CPLError() will have been called.
     369             :  **********************************************************************/
     370        1221 : int TABMAPToolBlock::CheckAvailableSpace(int nToolType)
     371             : {
     372        1221 :     int nBytesToWrite = 0;
     373             : 
     374        1221 :     switch (nToolType)
     375             :     {
     376          45 :         case TABMAP_TOOL_PEN:
     377          45 :             nBytesToWrite = 11;
     378          45 :             break;
     379          30 :         case TABMAP_TOOL_BRUSH:
     380          30 :             nBytesToWrite = 13;
     381          30 :             break;
     382           6 :         case TABMAP_TOOL_FONT:
     383           6 :             nBytesToWrite = 37;
     384           6 :             break;
     385        1140 :         case TABMAP_TOOL_SYMBOL:
     386        1140 :             nBytesToWrite = 13;
     387        1140 :             break;
     388           0 :         default:
     389           0 :             CPLAssert(false);
     390             :     }
     391             : 
     392        1221 :     if (GetNumUnusedBytes() < nBytesToWrite)
     393             :     {
     394           0 :         if (m_numBlocksInChain >= 255)
     395             :         {
     396           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     397             :                      "Maximum number of 255 tool blocks reached");
     398           0 :             return -1;
     399             :         }
     400           0 :         int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL");
     401           0 :         SetNextToolBlock(nNewBlockOffset);
     402             : 
     403           0 :         if (CommitToFile() != 0 ||
     404           0 :             InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
     405             :         {
     406             :             // An error message should have already been reported.
     407           0 :             return -1;
     408             :         }
     409             : 
     410           0 :         m_numBlocksInChain++;
     411             :     }
     412             : 
     413        1221 :     return 0;
     414             : }
     415             : 
     416             : /**********************************************************************
     417             :  *                   TABMAPToolBlock::Dump()
     418             :  *
     419             :  * Dump block contents... available only in DEBUG mode.
     420             :  **********************************************************************/
     421             : #ifdef DEBUG
     422             : 
     423           0 : void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/)
     424             : {
     425           0 :     if (fpOut == nullptr)
     426           0 :         fpOut = stdout;
     427             : 
     428           0 :     fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n");
     429           0 :     if (m_pabyBuf == nullptr)
     430             :     {
     431           0 :         fprintf(fpOut, "Block has not been initialized yet.");
     432             :     }
     433             :     else
     434             :     {
     435           0 :         fprintf(fpOut, "Tool Block (type %d) at offset %d.\n", m_nBlockType,
     436             :                 m_nFileOffset);
     437           0 :         fprintf(fpOut, "  m_numDataBytes        = %d\n", m_numDataBytes);
     438           0 :         fprintf(fpOut, "  m_nNextToolBlock     = %d\n", m_nNextToolBlock);
     439             :     }
     440             : 
     441           0 :     fflush(fpOut);
     442           0 : }
     443             : 
     444             : #endif  // DEBUG

Generated by: LCOV version 1.14