blob: e55544efd742be64b5b40124c5b3ed00adc67107 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "oox/xls/biffinputstream.hxx"
#include <algorithm>
#include <rtl/ustrbuf.hxx>
namespace oox {
namespace xls {
// ============================================================================
using ::rtl::OString;
using ::rtl::OStringToOUString;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// ============================================================================
namespace prv {
BiffInputRecordBuffer::BiffInputRecordBuffer( BinaryInputStream& rInStrm ) :
mrInStrm( rInStrm ),
mpCurrentData( 0 ),
mnHeaderPos( -1 ),
mnBodyPos( 0 ),
mnBufferBodyPos( 0 ),
mnNextHeaderPos( 0 ),
mnRecId( BIFF_ID_UNKNOWN ),
mnRecSize( 0 ),
mnRecPos( 0 ),
mbValidHeader( false )
{
OSL_ENSURE( mrInStrm.isSeekable(), "BiffInputRecordBuffer::BiffInputRecordBuffer - stream must be seekable" );
mrInStrm.seekToStart();
maOriginalData.reserve( SAL_MAX_UINT16 );
maDecodedData.reserve( SAL_MAX_UINT16 );
enableDecoder( false ); // updates mpCurrentData
}
void BiffInputRecordBuffer::restartAt( sal_Int64 nPos )
{
mnHeaderPos = -1;
mnBodyPos = mnBufferBodyPos = 0;
mnNextHeaderPos = nPos;
mnRecId = BIFF_ID_UNKNOWN;
mnRecSize = mnRecPos = 0;
mbValidHeader = false;
}
void BiffInputRecordBuffer::setDecoder( const BiffDecoderRef& rxDecoder )
{
mxDecoder = rxDecoder;
enableDecoder( true );
updateDecoded();
}
void BiffInputRecordBuffer::enableDecoder( bool bEnable )
{
mpCurrentData = (bEnable && mxDecoder.get() && mxDecoder->isValid()) ? &maDecodedData : &maOriginalData;
}
bool BiffInputRecordBuffer::startRecord( sal_Int64 nHeaderPos )
{
mbValidHeader = (0 <= nHeaderPos) && (nHeaderPos + 4 <= mrInStrm.size());
if( mbValidHeader )
{
mnHeaderPos = nHeaderPos;
mrInStrm.seek( nHeaderPos );
mrInStrm >> mnRecId >> mnRecSize;
mnBodyPos = mrInStrm.tell();
mnNextHeaderPos = mnBodyPos + mnRecSize;
mbValidHeader = !mrInStrm.isEof() && (mnNextHeaderPos <= mrInStrm.size());
}
if( !mbValidHeader )
{
mnHeaderPos = mnBodyPos = -1;
mnNextHeaderPos = 0;
mnRecId = BIFF_ID_UNKNOWN;
mnRecSize = 0;
}
mnRecPos = 0;
return mbValidHeader;
}
bool BiffInputRecordBuffer::startNextRecord()
{
return startRecord( mnNextHeaderPos );
}
sal_uInt16 BiffInputRecordBuffer::getNextRecId()
{
sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
if( mbValidHeader && (mnNextHeaderPos + 4 <= mrInStrm.size()) )
{
mrInStrm.seek( mnNextHeaderPos );
mrInStrm >> nRecId;
}
return nRecId;
}
void BiffInputRecordBuffer::read( void* opData, sal_uInt16 nBytes )
{
updateBuffer();
OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::read - nothing to read" );
OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::read - buffer overflow" );
memcpy( opData, &(*mpCurrentData)[ mnRecPos ], nBytes );
mnRecPos = mnRecPos + nBytes;
}
void BiffInputRecordBuffer::skip( sal_uInt16 nBytes )
{
OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::skip - nothing to skip" );
OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::skip - buffer overflow" );
mnRecPos = mnRecPos + nBytes;
}
void BiffInputRecordBuffer::updateBuffer()
{
OSL_ENSURE( mbValidHeader, "BiffInputRecordBuffer::updateBuffer - invalid access" );
if( mnBodyPos != mnBufferBodyPos )
{
mrInStrm.seek( mnBodyPos );
maOriginalData.resize( mnRecSize );
if( mnRecSize > 0 )
mrInStrm.readMemory( &maOriginalData.front(), static_cast< sal_Int32 >( mnRecSize ) );
mnBufferBodyPos = mnBodyPos;
updateDecoded();
}
}
void BiffInputRecordBuffer::updateDecoded()
{
if( mxDecoder.get() && mxDecoder->isValid() )
{
maDecodedData.resize( mnRecSize );
if( mnRecSize > 0 )
mxDecoder->decode( &maDecodedData.front(), &maOriginalData.front(), mnBodyPos, mnRecSize );
}
}
} // namespace prv
// ============================================================================
BiffInputStream::BiffInputStream( BinaryInputStream& rInStream, bool bContLookup ) :
BinaryStreamBase( true ),
maRecBuffer( rInStream ),
mnRecHandle( -1 ),
mnRecId( BIFF_ID_UNKNOWN ),
mnAltContId( BIFF_ID_UNKNOWN ),
mnCurrRecSize( 0 ),
mnComplRecSize( 0 ),
mbHasComplRec( false ),
mbCont( bContLookup )
{
mbEof = true; // EOF will be true if stream is not inside a record
}
// record control -------------------------------------------------------------
bool BiffInputStream::startNextRecord()
{
bool bValidRec = false;
/* #i4266# ignore zero records (id==len==0) (e.g. the application
"Crystal Report" writes zero records between other records) */
bool bIsZeroRec = false;
do
{
// record header is never encrypted
maRecBuffer.enableDecoder( false );
// read header of next raw record, returns false at end of stream
bValidRec = maRecBuffer.startNextRecord();
// ignore record, if identifier and size are zero
bIsZeroRec = (maRecBuffer.getRecId() == 0) && (maRecBuffer.getRecSize() == 0);
}
while( bValidRec && ((mbCont && isContinueId( maRecBuffer.getRecId() )) || bIsZeroRec) );
// setup other class members
setupRecord();
return isInRecord();
}
bool BiffInputStream::startRecordByHandle( sal_Int64 nRecHandle )
{
rewindToRecord( nRecHandle );
return startNextRecord();
}
void BiffInputStream::resetRecord( bool bContLookup, sal_uInt16 nAltContId )
{
if( isInRecord() )
{
mbCont = bContLookup;
mnAltContId = nAltContId;
restartRecord( true );
maRecBuffer.enableDecoder( true );
}
}
void BiffInputStream::rewindRecord()
{
rewindToRecord( mnRecHandle );
}
// decoder --------------------------------------------------------------------
void BiffInputStream::setDecoder( const BiffDecoderRef& rxDecoder )
{
maRecBuffer.setDecoder( rxDecoder );
}
void BiffInputStream::enableDecoder( bool bEnable )
{
maRecBuffer.enableDecoder( bEnable );
}
// stream/record state and info -----------------------------------------------
sal_uInt16 BiffInputStream::getNextRecId()
{
sal_uInt16 nRecId = BIFF_ID_UNKNOWN;
if( isInRecord() )
{
sal_Int64 nCurrPos = tell(); // save current position in record
while( jumpToNextContinue() ) {} // skip following CONTINUE records
if( maRecBuffer.startNextRecord() ) // read header of next record
nRecId = maRecBuffer.getRecId();
seek( nCurrPos ); // restore position, seek() resets old mbValid state
}
return nRecId;
}
// BinaryStreamBase interface (seeking) ---------------------------------------
sal_Int64 BiffInputStream::size() const
{
if( !mbHasComplRec )
const_cast< BiffInputStream* >( this )->calcRecordLength();
return mnComplRecSize;
}
sal_Int64 BiffInputStream::tell() const
{
return mbEof ? -1 : (mnCurrRecSize - maRecBuffer.getRecLeft());
}
void BiffInputStream::seek( sal_Int64 nRecPos )
{
if( isInRecord() )
{
if( mbEof || (nRecPos < tell()) )
restartRecord( false );
if( !mbEof && (nRecPos > tell()) )
skip( static_cast< sal_Int32 >( nRecPos - tell() ) );
}
}
void BiffInputStream::close()
{
}
sal_Int64 BiffInputStream::tellBase() const
{
return maRecBuffer.getBaseStream().tell();
}
sal_Int64 BiffInputStream::sizeBase() const
{
return maRecBuffer.getBaseStream().size();
}
// BinaryInputStream interface (stream read access) ---------------------------
sal_Int32 BiffInputStream::readData( StreamDataSequence& orData, sal_Int32 nBytes, size_t nAtomSize )
{
sal_Int32 nRet = 0;
if( !mbEof )
{
orData.realloc( ::std::max< sal_Int32 >( nBytes, 0 ) );
if( nBytes > 0 )
nRet = readMemory( orData.getArray(), nBytes, nAtomSize );
}
return nRet;
}
sal_Int32 BiffInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t nAtomSize )
{
sal_Int32 nRet = 0;
if( !mbEof && opMem && (nBytes > 0) )
{
sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( opMem );
sal_Int32 nBytesLeft = nBytes;
while( !mbEof && (nBytesLeft > 0) )
{
sal_uInt16 nReadSize = getMaxRawReadSize( nBytesLeft, nAtomSize );
// check nReadSize, stream may already be located at end of a raw record
if( nReadSize > 0 )
{
maRecBuffer.read( pnBuffer, nReadSize );
nRet += nReadSize;
pnBuffer += nReadSize;
nBytesLeft -= nReadSize;
}
if( nBytesLeft > 0 )
jumpToNextContinue();
OSL_ENSURE( !mbEof, "BiffInputStream::readMemory - record overread" );
}
}
return nRet;
}
void BiffInputStream::skip( sal_Int32 nBytes, size_t nAtomSize )
{
sal_Int32 nBytesLeft = nBytes;
while( !mbEof && (nBytesLeft > 0) )
{
sal_uInt16 nSkipSize = getMaxRawReadSize( nBytesLeft, nAtomSize );
// check nSkipSize, stream may already be located at end of a raw record
if( nSkipSize > 0 )
{
maRecBuffer.skip( nSkipSize );
nBytesLeft -= nSkipSize;
}
if( nBytesLeft > 0 )
jumpToNextContinue();
OSL_ENSURE( !mbEof, "BiffInputStream::skip - record overread" );
}
}
// byte strings ---------------------------------------------------------------
OString BiffInputStream::readByteString( bool b16BitLen, bool bAllowNulChars )
{
sal_Int32 nStrLen = b16BitLen ? readuInt16() : readuInt8();
return readCharArray( nStrLen, bAllowNulChars );
}
OUString BiffInputStream::readByteStringUC( bool b16BitLen, rtl_TextEncoding eTextEnc, bool bAllowNulChars )
{
return OStringToOUString( readByteString( b16BitLen, bAllowNulChars ), eTextEnc );
}
void BiffInputStream::skipByteString( bool b16BitLen )
{
skip( b16BitLen ? readuInt16() : readuInt8() );
}
// Unicode strings ------------------------------------------------------------
OUString BiffInputStream::readUniStringChars( sal_uInt16 nChars, bool b16BitChars, bool bAllowNulChars )
{
OUStringBuffer aBuffer;
aBuffer.ensureCapacity( nChars );
/* This function has to react on CONTINUE records which repeat the flags
field in their first byte and may change the 8bit/16bit character mode,
thus a plain call to readCompressedUnicodeArray() cannot be used here. */
sal_Int32 nCharsLeft = nChars;
while( !mbEof && (nCharsLeft > 0) )
{
/* Read the character array from the remaining part of the current raw
record. First, calculate the maximum number of characters that can
be read without triggering to start a following CONTINUE record. */
sal_Int32 nRawChars = b16BitChars ? (getMaxRawReadSize( nCharsLeft * 2, 2 ) / 2) : getMaxRawReadSize( nCharsLeft, 1 );
aBuffer.append( readCompressedUnicodeArray( nRawChars, !b16BitChars, bAllowNulChars ) );
/* Prepare for next CONTINUE record. Calling jumpToNextStringContinue()
reads the leading byte in the following CONTINUE record and updates
the b16BitChars flag. */
nCharsLeft -= nRawChars;
if( nCharsLeft > 0 )
jumpToNextStringContinue( b16BitChars );
}
return aBuffer.makeStringAndClear();
}
OUString BiffInputStream::readUniStringBody( sal_uInt16 nChars, bool bAllowNulChars )
{
bool b16BitChars;
sal_Int32 nAddSize;
readUniStringHeader( b16BitChars, nAddSize );
OUString aString = readUniStringChars( nChars, b16BitChars, bAllowNulChars );
skip( nAddSize );
return aString;
}
OUString BiffInputStream::readUniString( bool bAllowNulChars )
{
return readUniStringBody( readuInt16(), bAllowNulChars );
}
void BiffInputStream::skipUniStringChars( sal_uInt16 nChars, bool b16BitChars )
{
sal_Int32 nCharsLeft = nChars;
while( !mbEof && (nCharsLeft > 0) )
{
// skip the character array
sal_Int32 nSkipSize = b16BitChars ? getMaxRawReadSize( 2 * nCharsLeft, 2 ) : getMaxRawReadSize( nCharsLeft, 1 );
skip( nSkipSize );
// prepare for next CONTINUE record
nCharsLeft -= (b16BitChars ? (nSkipSize / 2) : nSkipSize);
if( nCharsLeft > 0 )
jumpToNextStringContinue( b16BitChars );
}
}
void BiffInputStream::skipUniStringBody( sal_uInt16 nChars )
{
bool b16BitChars;
sal_Int32 nAddSize;
readUniStringHeader( b16BitChars, nAddSize );
skipUniStringChars( nChars, b16BitChars );
skip( nAddSize );
}
void BiffInputStream::skipUniString()
{
skipUniStringBody( readuInt16() );
}
// private --------------------------------------------------------------------
void BiffInputStream::setupRecord()
{
// initialize class members
mnRecHandle = maRecBuffer.getRecHeaderPos();
mnRecId = maRecBuffer.getRecId();
mnAltContId = BIFF_ID_UNKNOWN;
mnCurrRecSize = mnComplRecSize = maRecBuffer.getRecSize();
mbHasComplRec = !mbCont;
mbEof = !isInRecord();
// enable decoder in new record
enableDecoder( true );
}
void BiffInputStream::restartRecord( bool bInvalidateRecSize )
{
if( isInRecord() )
{
maRecBuffer.startRecord( getRecHandle() );
mnCurrRecSize = maRecBuffer.getRecSize();
if( bInvalidateRecSize )
{
mnComplRecSize = mnCurrRecSize;
mbHasComplRec = !mbCont;
}
mbEof = false;
}
}
void BiffInputStream::rewindToRecord( sal_Int64 nRecHandle )
{
if( nRecHandle >= 0 )
{
maRecBuffer.restartAt( nRecHandle );
mnRecHandle = -1;
mbEof = true; // as long as the record is not started
}
}
bool BiffInputStream::isContinueId( sal_uInt16 nRecId ) const
{
return (nRecId == BIFF_ID_CONT) || (nRecId == mnAltContId);
}
bool BiffInputStream::jumpToNextContinue()
{
mbEof = mbEof || !mbCont || !isContinueId( maRecBuffer.getNextRecId() ) || !maRecBuffer.startNextRecord();
if( !mbEof )
mnCurrRecSize += maRecBuffer.getRecSize();
return !mbEof;
}
bool BiffInputStream::jumpToNextStringContinue( bool& rb16BitChars )
{
OSL_ENSURE( maRecBuffer.getRecLeft() == 0, "BiffInputStream::jumpToNextStringContinue - alignment error" );
if( mbCont && (getRemaining() > 0) )
{
jumpToNextContinue();
}
else if( mnRecId == BIFF_ID_CONT )
{
/* CONTINUE handling is off, but we have started reading in a CONTINUE
record -> start next CONTINUE for TXO import. We really start a new
record here - no chance to return to string origin. */
mbEof = mbEof || (maRecBuffer.getNextRecId() != BIFF_ID_CONT) || !maRecBuffer.startNextRecord();
if( !mbEof )
setupRecord();
}
// trying to read the flags invalidates stream, if no CONTINUE record has been found
sal_uInt8 nFlags;
readValue( nFlags );
rb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
return !mbEof;
}
void BiffInputStream::calcRecordLength()
{
sal_Int64 nCurrPos = tell(); // save current position in record
while( jumpToNextContinue() ) {} // jumpToNextContinue() adds up mnCurrRecSize
mnComplRecSize = mnCurrRecSize;
mbHasComplRec = true;
seek( nCurrPos ); // restore position, seek() resets old mbValid state
}
sal_uInt16 BiffInputStream::getMaxRawReadSize( sal_Int32 nBytes, size_t nAtomSize ) const
{
sal_uInt16 nMaxSize = getLimitedValue< sal_uInt16, sal_Int32 >( nBytes, 0, maRecBuffer.getRecLeft() );
if( (0 < nMaxSize) && (nMaxSize < nBytes) && (nAtomSize > 1) )
{
// check that remaining data in record buffer is a multiple of the passed atom size
sal_uInt16 nPadding = static_cast< sal_uInt16 >( nMaxSize % nAtomSize );
OSL_ENSURE( nPadding == 0, "BiffInputStream::getMaxRawReadSize - alignment error" );
nMaxSize = nMaxSize - nPadding;
}
return nMaxSize;
}
void BiffInputStream::readUniStringHeader( bool& orb16BitChars, sal_Int32& ornAddSize )
{
sal_uInt8 nFlags = readuInt8();
OSL_ENSURE( !getFlag( nFlags, BIFF_STRF_UNKNOWN ), "BiffInputStream::readUniStringHeader - unknown flags" );
orb16BitChars = getFlag( nFlags, BIFF_STRF_16BIT );
sal_uInt16 nFontCount = getFlag( nFlags, BIFF_STRF_RICH ) ? readuInt16() : 0;
sal_Int32 nPhoneticSize = getFlag( nFlags, BIFF_STRF_PHONETIC ) ? readInt32() : 0;
ornAddSize = 4 * nFontCount + ::std::max< sal_Int32 >( 0, nPhoneticSize );
}
// ============================================================================
BiffInputStreamPos::BiffInputStreamPos( BiffInputStream& rStrm ) :
mrStrm( rStrm ),
mnRecHandle( rStrm.getRecHandle() ),
mnRecPos( rStrm.tell() )
{
}
bool BiffInputStreamPos::restorePosition()
{
bool bValidRec = mrStrm.startRecordByHandle( mnRecHandle );
if( bValidRec )
mrStrm.seek( mnRecPos );
return bValidRec && !mrStrm.isEof();
}
// ============================================================================
BiffInputStreamPosGuard::BiffInputStreamPosGuard( BiffInputStream& rStrm ) :
BiffInputStreamPos( rStrm )
{
}
BiffInputStreamPosGuard::~BiffInputStreamPosGuard()
{
restorePosition();
}
// ============================================================================
} // namespace xls
} // namespace oox