/************************************************************** * * 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/ole/vbainputstream.hxx" #include namespace oox { namespace ole { // ============================================================================ namespace { const sal_uInt8 VBASTREAM_SIGNATURE = 1; const sal_uInt16 VBACHUNK_SIGMASK = 0x7000; const sal_uInt16 VBACHUNK_SIG = 0x3000; const sal_uInt16 VBACHUNK_COMPRESSED = 0x8000; const sal_uInt16 VBACHUNK_LENMASK = 0x0FFF; } // namespace // ============================================================================ VbaInputStream::VbaInputStream( BinaryInputStream& rInStrm ) : BinaryStreamBase( false ), mpInStrm( &rInStrm ), mnChunkPos( 0 ) { maChunk.reserve( 4096 ); sal_uInt8 nSig = rInStrm.readuInt8(); OSL_ENSURE( nSig == VBASTREAM_SIGNATURE, "VbaInputStream::VbaInputStream - wrong signature" ); mbEof = mbEof || rInStrm.isEof() || (nSig != VBASTREAM_SIGNATURE); } sal_Int64 VbaInputStream::size() const { return -1; } sal_Int64 VbaInputStream::tell() const { return -1; } void VbaInputStream::seek( sal_Int64 ) { } void VbaInputStream::close() { mpInStrm = 0; mbEof = true; } sal_Int32 VbaInputStream::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 ); if( nRet < nBytes ) orData.realloc( nRet ); } } return nRet; } sal_Int32 VbaInputStream::readMemory( void* opMem, sal_Int32 nBytes, size_t /*nAtomSize*/ ) { sal_Int32 nRet = 0; sal_uInt8* opnMem = reinterpret_cast< sal_uInt8* >( opMem ); while( (nBytes > 0) && updateChunk() ) { sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); sal_Int32 nReadBytes = ::std::min( nBytes, nChunkLeft ); memcpy( opnMem, &*(maChunk.begin() + mnChunkPos), nReadBytes ); opnMem += nReadBytes; mnChunkPos += static_cast< size_t >( nReadBytes ); nBytes -= nReadBytes; nRet += nReadBytes; } return nRet; } void VbaInputStream::skip( sal_Int32 nBytes, size_t /*nAtomSize*/ ) { while( (nBytes > 0) && updateChunk() ) { sal_Int32 nChunkLeft = static_cast< sal_Int32 >( maChunk.size() - mnChunkPos ); sal_Int32 nSkipBytes = ::std::min( nBytes, nChunkLeft ); mnChunkPos += static_cast< size_t >( nSkipBytes ); nBytes -= nSkipBytes; } } // private -------------------------------------------------------------------- bool VbaInputStream::updateChunk() { if( mbEof || (mnChunkPos < maChunk.size()) ) return !mbEof; // try to read next chunk header, this may trigger EOF sal_uInt16 nHeader = mpInStrm->readuInt16(); mbEof = mpInStrm->isEof(); if( mbEof ) return false; // check header signature OSL_ENSURE( (nHeader & VBACHUNK_SIGMASK) == VBACHUNK_SIG, "VbaInputStream::updateChunk - invalid chunk signature" ); mbEof = (nHeader & VBACHUNK_SIGMASK) != VBACHUNK_SIG; if( mbEof ) return false; // decode length of chunk data and compression flag bool bCompressed = getFlag( nHeader, VBACHUNK_COMPRESSED ); sal_uInt16 nChunkLen = (nHeader & VBACHUNK_LENMASK) + 1; OSL_ENSURE( bCompressed || (nChunkLen == 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" ); if( bCompressed ) { maChunk.clear(); sal_uInt8 nBitCount = 4; sal_uInt16 nChunkPos = 0; while( !mbEof && !mpInStrm->isEof() && (nChunkPos < nChunkLen) ) { sal_uInt8 nTokenFlags = mpInStrm->readuInt8(); ++nChunkPos; for( int nBit = 0; !mbEof && !mpInStrm->isEof() && (nBit < 8) && (nChunkPos < nChunkLen); ++nBit, nTokenFlags >>= 1 ) { if( nTokenFlags & 1 ) { sal_uInt16 nCopyToken = mpInStrm->readuInt16(); nChunkPos = nChunkPos + 2; // update bit count used for offset/length in the token while( static_cast< size_t >( 1 << nBitCount ) < maChunk.size() ) ++nBitCount; // extract length from lower (16-nBitCount) bits, plus 3 sal_uInt16 nLength = extractValue< sal_uInt16 >( nCopyToken, 0, 16 - nBitCount ) + 3; // extract offset from high nBitCount bits, plus 1 sal_uInt16 nOffset = extractValue< sal_uInt16 >( nCopyToken, 16 - nBitCount, nBitCount ) + 1; mbEof = (nOffset > maChunk.size()) || (maChunk.size() + nLength > 4096); OSL_ENSURE( !mbEof, "VbaInputStream::updateChunk - invalid offset or size in copy token" ); if( !mbEof ) { // append data to buffer maChunk.resize( maChunk.size() + nLength ); sal_uInt8* pnTo = &*(maChunk.end() - nLength); const sal_uInt8* pnEnd = pnTo + nLength; const sal_uInt8* pnFrom = pnTo - nOffset; // offset may be less than length, effectively duplicating source data several times size_t nRunLen = ::std::min< size_t >( nLength, nOffset ); while( pnTo < pnEnd ) { size_t nStepLen = ::std::min< size_t >( nRunLen, pnEnd - pnTo ); memcpy( pnTo, pnFrom, nStepLen ); pnTo += nStepLen; } } } else { maChunk.resize( maChunk.size() + 1 ); *mpInStrm >> maChunk.back(); ++nChunkPos; } } } } else { maChunk.resize( nChunkLen ); mpInStrm->readMemory( &maChunk.front(), nChunkLen ); } mnChunkPos = 0; return !mbEof; } // ============================================================================ } // namespace ole } // namespace oox