www.pudn.com > mitab-1.5.1.zip > mitab_rawbinblock.cpp
/**********************************************************************
* $Id: mitab_rawbinblock.cpp,v 1.8 2005/10/06 19:15:31 dmorissette Exp $
*
* Name: mitab_rawbinblock.cpp
* Project: MapInfo TAB Read/Write library
* Language: C++
* Purpose: Implementation of the TABRawBinBlock class used to handle
* reading/writing blocks in the .MAP files
* Author: Daniel Morissette, dmorissette@dmsolutions.ca
*
**********************************************************************
* Copyright (c) 1999, 2000, Daniel Morissette
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**********************************************************************
*
* $Log: mitab_rawbinblock.cpp,v $
* Revision 1.8 2005/10/06 19:15:31 dmorissette
* Collections: added support for reading/writing pen/brush/symbol ids and
* for writing collection objects to .TAB/.MAP (bug 1126)
*
* Revision 1.7 2004/12/01 18:25:03 dmorissette
* Fixed potential memory leaks in error conditions (bug 881)
*
* Revision 1.6 2004/06/30 20:29:04 dmorissette
* Fixed refs to old address danmo@videotron.ca
*
* Revision 1.5 2000/02/28 17:06:06 daniel
* Added m_bModified flag
*
* Revision 1.4 2000/01/15 22:30:45 daniel
* Switch to MIT/X-Consortium OpenSource license
*
* Revision 1.3 1999/09/26 14:59:37 daniel
* Implemented write support
*
* Revision 1.2 1999/09/16 02:39:17 daniel
* Completed read support for most feature types
*
* Revision 1.1 1999/07/12 04:18:25 daniel
* Initial checkin
*
**********************************************************************/
#include "mitab.h"
/*=====================================================================
* class TABRawBinBlock
*====================================================================*/
/**********************************************************************
* TABRawBinBlock::TABRawBinBlock()
*
* Constructor.
**********************************************************************/
TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/,
GBool bHardBlockSize /*= TRUE*/)
{
m_fp = NULL;
m_pabyBuf = NULL;
m_nFirstBlockPtr = 0;
m_nBlockSize = m_nSizeUsed = m_nFileOffset = m_nCurPos = 0;
m_bHardBlockSize = bHardBlockSize;
m_bModified = FALSE;
m_eAccess = eAccessMode;
}
/**********************************************************************
* TABRawBinBlock::~TABRawBinBlock()
*
* Destructor.
**********************************************************************/
TABRawBinBlock::~TABRawBinBlock()
{
if (m_pabyBuf)
CPLFree(m_pabyBuf);
}
/**********************************************************************
* TABRawBinBlock::ReadFromFile()
*
* Load data from the specified file location and initialize the block.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::ReadFromFile(FILE *fpSrc, int nOffset,
int nSize /*= 512*/)
{
GByte *pabyBuf;
if (fpSrc == NULL || nSize == 0)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"TABRawBinBlock::ReadFromFile(): Assertion Failed!");
return -1;
}
m_fp = fpSrc;
m_nFileOffset = nOffset;
m_nCurPos = 0;
m_bModified = FALSE;
/*----------------------------------------------------------------
* Alloc a buffer to contain the data
*---------------------------------------------------------------*/
pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
/*----------------------------------------------------------------
* Read from the file
*---------------------------------------------------------------*/
if (VSIFSeek(fpSrc, nOffset, SEEK_SET) != 0 ||
(m_nSizeUsed = VSIFRead(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 ||
(m_bHardBlockSize && m_nSizeUsed != nSize ) )
{
CPLError(CE_Failure, CPLE_FileIO,
"ReadFromFile() failed reading %d bytes at offset %d.",
nSize, nOffset);
CPLFree(pabyBuf);
return -1;
}
/*----------------------------------------------------------------
* Init block with the data we just read
*---------------------------------------------------------------*/
return InitBlockFromData(pabyBuf, nSize, FALSE, fpSrc, nOffset);
}
/**********************************************************************
* TABRawBinBlock::CommitToFile()
*
* Commit the current state of the binary block to the file to which
* it has been previously attached.
*
* Derived classes may want to (optionally) reimplement this method if
* they need to do special processing before committing the block to disk.
*
* For files created with bHardBlockSize=TRUE, a complete block of
* the specified size is always written, otherwise only the number of
* used bytes in the block will be written to disk.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::CommitToFile()
{
int nStatus = 0;
if (m_fp == NULL || m_nBlockSize <= 0 || m_pabyBuf == NULL ||
m_nFileOffset < 0)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"TABRawBinBlock::CommitToFile(): Block has not been initialized yet!");
return -1;
}
/*----------------------------------------------------------------
* If block has not been modified, then just return... nothing to do.
*---------------------------------------------------------------*/
if (!m_bModified)
return 0;
/*----------------------------------------------------------------
* Move the output file pointer to the right position...
*---------------------------------------------------------------*/
if (VSIFSeek(m_fp, m_nFileOffset, SEEK_SET) != 0)
{
/*------------------------------------------------------------
* Moving pointer failed... we may need to pad with zeros if
* block destination is beyond current end of file.
*-----------------------------------------------------------*/
int nCurPos;
nCurPos = VSIFTell(m_fp);
if (nCurPos < m_nFileOffset &&
VSIFSeek(m_fp, 0L, SEEK_END) == 0 &&
(nCurPos = VSIFTell(m_fp)) < m_nFileOffset)
{
GByte cZero = 0;
while(nCurPos < m_nFileOffset && nStatus == 0)
{
if (VSIFWrite(&cZero, 1, 1, m_fp) != 1)
{
CPLError(CE_Failure, CPLE_FileIO,
"Failed writing 1 byte at offset %d.", nCurPos);
nStatus = -1;
break;
}
nCurPos++;
}
}
if (nCurPos != m_nFileOffset)
nStatus = -1; // Error message will follow below
}
/*----------------------------------------------------------------
* At this point we are ready to write to the file.
*
* If m_bHardBlockSize==FALSE, then we do not write a complete block;
* we write only the part of the block that was used.
*---------------------------------------------------------------*/
int numBytesToWrite = m_bHardBlockSize?m_nBlockSize:m_nSizeUsed;
if (nStatus != 0 ||
VSIFWrite(m_pabyBuf,sizeof(GByte),
numBytesToWrite, m_fp) != (size_t)numBytesToWrite )
{
CPLError(CE_Failure, CPLE_FileIO,
"Failed writing %d bytes at offset %d.",
numBytesToWrite, m_nFileOffset);
return -1;
}
fflush(m_fp);
m_bModified = FALSE;
return 0;
}
/**********************************************************************
* TABRawBinBlock::InitBlockFromData()
*
* Set the binary data buffer and initialize the block.
*
* Calling ReadFromFile() will automatically call InitBlockFromData() to
* complete the initialization of the block after the data is read from the
* file. Derived classes should implement their own version of
* InitBlockFromData() if they need specific initialization... in this
* case the derived InitBlockFromData() should call TABRawBinBlock::InitBlockFromData()
* before doing anything else.
*
* By default, the buffer will be copied, but if bMakeCopy = FALSE then
* it won't be copied, and the object will keep a reference to the
* user's buffer... and this object will eventually free the user's buffer.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, int nSize,
GBool bMakeCopy /* = TRUE */,
FILE *fpSrc /* = NULL */,
int nOffset /* = 0 */)
{
m_fp = fpSrc;
m_nFileOffset = nOffset;
m_nCurPos = 0;
m_bModified = FALSE;
/*----------------------------------------------------------------
* Alloc or realloc the buffer to contain the data if necessary
*---------------------------------------------------------------*/
if (!bMakeCopy)
{
if (m_pabyBuf != NULL)
CPLFree(m_pabyBuf);
m_pabyBuf = pabyBuf;
m_nSizeUsed = m_nBlockSize = nSize;
}
else if (m_pabyBuf == NULL || nSize != m_nBlockSize)
{
m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, nSize*sizeof(GByte));
m_nSizeUsed = m_nBlockSize = nSize;
memcpy(m_pabyBuf, pabyBuf, m_nBlockSize);
}
/*----------------------------------------------------------------
* Extract block type... header block (first block in a file) has
* no block type, so we assign one by default.
*---------------------------------------------------------------*/
if (m_nFileOffset == 0)
m_nBlockType = TABMAP_HEADER_BLOCK;
else
{
// Block type will be validated only if GetBlockType() is called
m_nBlockType = (int)m_pabyBuf[0];
}
return 0;
}
/**********************************************************************
* TABRawBinBlock::InitNewBlock()
*
* Initialize the block so that it knows to which file is is attached,
* its block size, etc.
*
* This is an alternative to calling ReadFromFile() or InitBlockFromData()
* that puts the block in a stable state without loading any initial
* data in it.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::InitNewBlock(FILE *fpSrc, int nBlockSize,
int nFileOffset /* = 0*/)
{
m_fp = fpSrc;
m_nBlockSize = nBlockSize;
m_nSizeUsed = 0;
m_nCurPos = 0;
m_bModified = FALSE;
if (nFileOffset > 0)
m_nFileOffset = nFileOffset;
else
m_nFileOffset = 0;
m_nBlockType = -1;
m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, m_nBlockSize*sizeof(GByte));
memset(m_pabyBuf, 0, m_nBlockSize);
return 0;
}
/**********************************************************************
* TABRawBinBlock::GetBlockType()
*
* Return the block type for the current object.
*
* Returns a block type >= 0 if succesful or -1 if an error happened, in
* which case CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::GetBlockType()
{
if (m_pabyBuf == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"GetBlockType(): Block has not been initialized.");
return -1;
}
if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE)
{
CPLError(CE_Failure, CPLE_NotSupported,
"GetBlockType(): Unsupported block type %d.",
m_nBlockType);
return -1;
}
return m_nBlockType;
}
/**********************************************************************
* TABRawBinBlock::GotoByteInBlock()
*
* Move the block pointer to the specified position relative to the
* beginning of the block.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::GotoByteInBlock(int nOffset)
{
if ( (m_eAccess == TABRead && nOffset > m_nSizeUsed) ||
(m_eAccess != TABRead && nOffset > m_nBlockSize) )
{
CPLError(CE_Failure, CPLE_AppDefined,
"GotoByteInBlock(): Attempt to go past end of data block.");
return -1;
}
if (nOffset < 0)
{
CPLError(CE_Failure, CPLE_AppDefined,
"GotoByteInBlock(): Attempt to go before start of data block.");
return -1;
}
m_nCurPos = nOffset;
m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
return 0;
}
/**********************************************************************
* TABRawBinBlock::GotoByteRel()
*
* Move the block pointer by the specified number of bytes relative
* to its current position.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::GotoByteRel(int nOffset)
{
return GotoByteInBlock(m_nCurPos + nOffset);
}
/**********************************************************************
* TABRawBinBlock::GotoByteInFile()
*
* Move the block pointer to the specified position relative to the
* beginning of the file.
*
* In read access, the current block may be reloaded to contain a right
* block of binary data if necessary.
*
* In write mode, the current block may automagically be committed to
* disk and a new block initialized if necessary.
*
* bForceReadFromFile is used in write mode to read the new block data from
* file instead of creating an empty block. (Useful for TABCollection
* or other cases that need to do random access in the file in write mode.)
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::GotoByteInFile(int nOffset,
GBool bForceReadFromFile /*=FALSE*/)
{
int nNewBlockPtr;
if (nOffset < 0)
{
CPLError(CE_Failure, CPLE_AppDefined,
"GotoByteInFile(): Attempt to go before start of file.");
return -1;
}
nNewBlockPtr = ( (nOffset-m_nFirstBlockPtr)/m_nBlockSize)*m_nBlockSize +
m_nFirstBlockPtr;
if (m_eAccess == TABRead)
{
if ( (nOffset=m_nFileOffset+m_nSizeUsed) &&
ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)
{
// Failed reading new block... error has already been reported.
return -1;
}
}
else if (m_eAccess == TABWrite)
{
if ( (nOffset=m_nFileOffset+m_nBlockSize) &&
(CommitToFile() != 0 ||
InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0 ) )
{
// Failed reading new block... error has already been reported.
return -1;
}
}
else if (m_eAccess == TABReadWrite)
{
// TODO: THIS IS NOT REAL read/write access (it's more extended write)
// Currently we try to read from file only if explicitly requested.
// If we ever want true read/write mode we should implement
// more smarts to detect whether the caller wants an existing block to
// be read, or a new one to be created from scratch.
// CommitToFile() should only be called only if something changed.
//
if ( (nOffset=m_nFileOffset+m_nBlockSize) &&
(CommitToFile() != 0 ||
(!bForceReadFromFile &&
InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
(bForceReadFromFile &&
ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) )
{
// Failed reading new block... error has already been reported.
return -1;
}
}
else
{
CPLError(CE_Failure, CPLE_NotSupported,
"Access mode not supported yet!");
return -1;
}
m_nCurPos = nOffset-m_nFileOffset;
m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
return 0;
}
/**********************************************************************
* TABRawBinBlock::SetFirstBlockPtr()
*
* Set the position in the file at which the first block starts.
* This value will usually be the header size and needs to be specified
* only if the header size is different from the other blocks size.
*
* This value will be used by GotoByteInFile() to properly align the data
* blocks that it loads automatically when a requested position is outside
* of the block currently in memory.
**********************************************************************/
void TABRawBinBlock::SetFirstBlockPtr(int nOffset)
{
m_nFirstBlockPtr = nOffset;
}
/**********************************************************************
* TABRawBinBlock::GetNumUnusedBytes()
*
* Return the number of unused bytes in this block.
**********************************************************************/
int TABRawBinBlock::GetNumUnusedBytes()
{
return (m_nBlockSize - m_nSizeUsed);
}
/**********************************************************************
* TABRawBinBlock::GetFirstUnusedByteOffset()
*
* Return the position of the first unused byte in this block relative
* to the beginning of the file, or -1 if the block is full.
**********************************************************************/
int TABRawBinBlock::GetFirstUnusedByteOffset()
{
if (m_nSizeUsed < m_nBlockSize)
return m_nFileOffset + m_nSizeUsed;
else
return -1;
}
/**********************************************************************
* TABRawBinBlock::GetCurAddress()
*
* Return the current pointer position, relative to beginning of file.
**********************************************************************/
int TABRawBinBlock::GetCurAddress()
{
return (m_nFileOffset + m_nCurPos);
}
/**********************************************************************
* TABRawBinBlock::ReadBytes()
*
* Copy the number of bytes from the data block's internal buffer to
* the user's buffer pointed by pabyDstBuf.
*
* Passing pabyDstBuf = NULL will only move the read pointer by the
* specified number of bytes as if the copy had happened... but it
* won't crash.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
{
/*----------------------------------------------------------------
* Make sure block is initialized with Read access and that the
* operation won't go beyond the buffer's size.
*---------------------------------------------------------------*/
if (m_pabyBuf == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"ReadBytes(): Block has not been initialized.");
return -1;
}
if (m_eAccess != TABRead && m_eAccess != TABReadWrite )
{
CPLError(CE_Failure, CPLE_AppDefined,
"ReadBytes(): Block does not support read operations.");
return -1;
}
if (m_nCurPos + numBytes > m_nSizeUsed)
{
CPLError(CE_Failure, CPLE_AppDefined,
"ReadBytes(): Attempt to read past end of data block.");
return -1;
}
if (pabyDstBuf)
{
memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes);
}
m_nCurPos += numBytes;
return 0;
}
/**********************************************************************
* TABRawBinBlock::Read()
*
* MapInfo files are binary files with LSB first (Intel) byte
* ordering. The following functions will read from the input file
* and return a value with the bytes ordered properly for the current
* platform.
**********************************************************************/
GByte TABRawBinBlock::ReadByte()
{
GByte byValue;
ReadBytes(1, (GByte*)(&byValue));
return byValue;
}
GInt16 TABRawBinBlock::ReadInt16()
{
GInt16 n16Value;
ReadBytes(2, (GByte*)(&n16Value));
#ifdef CPL_MSB
return (GInt16)CPL_SWAP16(n16Value);
#else
return n16Value;
#endif
}
GInt32 TABRawBinBlock::ReadInt32()
{
GInt32 n32Value;
ReadBytes(4, (GByte*)(&n32Value));
#ifdef CPL_MSB
return (GInt32)CPL_SWAP32(n32Value);
#else
return n32Value;
#endif
}
float TABRawBinBlock::ReadFloat()
{
float fValue;
ReadBytes(4, (GByte*)(&fValue));
#ifdef CPL_MSB
*(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
#endif
return fValue;
}
double TABRawBinBlock::ReadDouble()
{
double dValue;
ReadBytes(8, (GByte*)(&dValue));
#ifdef CPL_MSB
CPL_SWAPDOUBLE(&dValue);
#endif
return dValue;
}
/**********************************************************************
* TABRawBinBlock::WriteBytes()
*
* Copy the number of bytes from the user's buffer pointed by pabySrcBuf
* to the data block's internal buffer.
* Note that this call only writes to the memory buffer... nothing is
* written to the file until WriteToFile() is called.
*
* Passing pabySrcBuf = NULL will only move the write pointer by the
* specified number of bytes as if the copy had happened... but it
* won't crash.
*
* Returns 0 if succesful or -1 if an error happened, in which case
* CPLError() will have been called.
**********************************************************************/
int TABRawBinBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf)
{
/*----------------------------------------------------------------
* Make sure block is initialized with Write access and that the
* operation won't go beyond the buffer's size.
*---------------------------------------------------------------*/
if (m_pabyBuf == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"WriteBytes(): Block has not been initialized.");
return -1;
}
if (m_eAccess != TABWrite && m_eAccess != TABReadWrite )
{
CPLError(CE_Failure, CPLE_AppDefined,
"WriteBytes(): Block does not support write operations.");
return -1;
}
if (m_nCurPos + nBytesToWrite > m_nBlockSize)
{
CPLError(CE_Failure, CPLE_AppDefined,
"WriteBytes(): Attempt to write past end of data block.");
return -1;
}
/*----------------------------------------------------------------
* Everything is OK... copy the data
*---------------------------------------------------------------*/
if (pabySrcBuf)
{
memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite);
}
m_nCurPos += nBytesToWrite;
m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos);
m_bModified = TRUE;
return 0;
}
/**********************************************************************
* TABRawBinBlock::Write()
*
* Arc/Info files are binary files with MSB first (Motorola) byte
* ordering. The following functions will reorder the byte for the
* value properly and write that to the output file.
*
* If a problem happens, then CPLError() will be called and
* CPLGetLastErrNo() can be used to test if a write operation was
* succesful.
**********************************************************************/
int TABRawBinBlock::WriteByte(GByte byValue)
{
return WriteBytes(1, (GByte*)&byValue);
}
int TABRawBinBlock::WriteInt16(GInt16 n16Value)
{
#ifdef CPL_MSB
n16Value = (GInt16)CPL_SWAP16(n16Value);
#endif
return WriteBytes(2, (GByte*)&n16Value);
}
int TABRawBinBlock::WriteInt32(GInt32 n32Value)
{
#ifdef CPL_MSB
n32Value = (GInt32)CPL_SWAP32(n32Value);
#endif
return WriteBytes(4, (GByte*)&n32Value);
}
int TABRawBinBlock::WriteFloat(float fValue)
{
#ifdef CPL_MSB
*(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue));
#endif
return WriteBytes(4, (GByte*)&fValue);
}
int TABRawBinBlock::WriteDouble(double dValue)
{
#ifdef CPL_MSB
CPL_SWAPDOUBLE(&dValue);
#endif
return WriteBytes(8, (GByte*)&dValue);
}
/**********************************************************************
* TABRawBinBlock::WriteZeros()
*
* Write a number of zeros (sepcified in bytes) at the current position
* in the file.
*
* If a problem happens, then CPLError() will be called and
* CPLGetLastErrNo() can be used to test if a write operation was
* succesful.
**********************************************************************/
int TABRawBinBlock::WriteZeros(int nBytesToWrite)
{
char acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
int i;
int nStatus = 0;
/* Write by 8 bytes chunks. The last chunk may be less than 8 bytes
*/
for(i=0; nStatus == 0 && i< nBytesToWrite; i+=8)
{
nStatus = WriteBytes(MIN(8,(nBytesToWrite-i)), (GByte*)acZeros);
}
return nStatus;
}
/**********************************************************************
* TABRawBinBlock::WritePaddedString()
*
* Write a string and pad the end of the field (up to nFieldSize) with
* spaces number of spaces at the current position in the file.
*
* If a problem happens, then CPLError() will be called and
* CPLGetLastErrNo() can be used to test if a write operation was
* succesful.
**********************************************************************/
int TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString)
{
char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
int i, nLen, numSpaces;
int nStatus = 0;
nLen = strlen(pszString);
nLen = MIN(nLen, nFieldSize);
numSpaces = nFieldSize - nLen;
if (nLen > 0)
nStatus = WriteBytes(nLen, (GByte*)pszString);
/* Write spaces by 8 bytes chunks. The last chunk may be less than 8 bytes
*/
for(i=0; nStatus == 0 && i< numSpaces; i+=8)
{
nStatus = WriteBytes(MIN(8,(numSpaces-i)), (GByte*)acSpaces);
}
return nStatus;
}
/**********************************************************************
* TABRawBinBlock::Dump()
*
* Dump block contents... available only in DEBUG mode.
**********************************************************************/
#ifdef DEBUG
void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/)
{
if (fpOut == NULL)
fpOut = stdout;
fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n");
if (m_pabyBuf == NULL)
{
fprintf(fpOut, "Block has not been initialized yet.");
}
else
{
fprintf(fpOut, "Block (type %d) size=%d bytes at offset %d in file.\n",
m_nBlockType, m_nBlockSize, m_nFileOffset);
fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos);
}
fflush(fpOut);
}
#endif // DEBUG
/**********************************************************************
* DumpBytes()
*
* Read and dump the contents of an Binary file.
**********************************************************************/
void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/,
FILE *fpOut /*=NULL*/)
{
GInt32 anVal[2];
GInt16 *pn16Val1, *pn16Val2;
float *pfValue;
char *pcValue;
double *pdValue;
pfValue = (float*)(&nValue);
pcValue = (char*)(&nValue);
pdValue = (double*)anVal;
pn16Val1 = (GInt16*)(pcValue+2);
pn16Val2 = (GInt16*)(pcValue);
anVal[0] = anVal[1] = 0;
/* For double precision values, we only use the first half
* of the height bytes... and leave the other 4 bytes as zeros!
* It's a bit of a hack, but it seems to be enough for the
* precision of the values we print!
*/
#ifdef CPL_MSB
anVal[0] = nValue;
#else
anVal[1] = nValue;
#endif
if (fpOut == NULL)
fpOut = stdout;
fprintf(fpOut, "%d\t0x%8.8x %-5d\t%-6d %-6d %5.3e d=%5.3e",
nOffset, nValue, nValue,
*pn16Val1, *pn16Val2, *pfValue, *pdValue);
printf("\t[%c%c%c%c]\n", isprint(pcValue[0])?pcValue[0]:'.',
isprint(pcValue[1])?pcValue[1]:'.',
isprint(pcValue[2])?pcValue[2]:'.',
isprint(pcValue[3])?pcValue[3]:'.');
}
/**********************************************************************
* TABCreateMAPBlockFromFile()
*
* Load data from the specified file location and create and initialize
* a TABMAP*Block of the right type to handle it.
*
* Returns the new object if succesful or NULL if an error happened, in
* which case CPLError() will have been called.
**********************************************************************/
TABRawBinBlock *TABCreateMAPBlockFromFile(FILE *fpSrc, int nOffset,
int nSize /*= 512*/,
GBool bHardBlockSize /*= TRUE */,
TABAccess eAccessMode /*= TABRead*/)
{
TABRawBinBlock *poBlock = NULL;
GByte *pabyBuf;
if (fpSrc == NULL || nSize == 0)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"TABCreateMAPBlockFromFile(): Assertion Failed!");
return NULL;
}
/*----------------------------------------------------------------
* Alloc a buffer to contain the data
*---------------------------------------------------------------*/
pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte));
/*----------------------------------------------------------------
* Read from the file
*---------------------------------------------------------------*/
if (VSIFSeek(fpSrc, nOffset, SEEK_SET) != 0 ||
VSIFRead(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize )
{
CPLError(CE_Failure, CPLE_FileIO,
"TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.",
nSize, nOffset);
CPLFree(pabyBuf);
return NULL;
}
/*----------------------------------------------------------------
* Create an object of the right type
* Header block is different: it does not start with the object
* type byte but it is always the first block in a file
*---------------------------------------------------------------*/
if (nOffset == 0)
{
poBlock = new TABMAPHeaderBlock;
}
else
{
switch(pabyBuf[0])
{
case TABMAP_INDEX_BLOCK:
poBlock = new TABMAPIndexBlock(eAccessMode);
break;
case TABMAP_OBJECT_BLOCK:
poBlock = new TABMAPObjectBlock(eAccessMode);
break;
case TABMAP_COORD_BLOCK:
poBlock = new TABMAPCoordBlock(eAccessMode);
break;
case TABMAP_TOOL_BLOCK:
poBlock = new TABMAPToolBlock(eAccessMode);
break;
case TABMAP_GARB_BLOCK:
default:
poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize);
break;
}
}
/*----------------------------------------------------------------
* Init new object with the data we just read
*---------------------------------------------------------------*/
if (poBlock->InitBlockFromData(pabyBuf, nSize, FALSE, fpSrc, nOffset) != 0)
{
// Some error happened... and CPLError() has been called
delete poBlock;
poBlock = NULL;
}
return poBlock;
}