1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "oox/ole/vbainputstream.hxx"
21 #include <osl/diagnose.h>
28 const sal_uInt8 VBASTREAM_SIGNATURE
= 1;
30 const sal_uInt16 VBACHUNK_SIGMASK
= 0x7000;
31 const sal_uInt16 VBACHUNK_SIG
= 0x3000;
32 const sal_uInt16 VBACHUNK_COMPRESSED
= 0x8000;
33 const sal_uInt16 VBACHUNK_LENMASK
= 0x0FFF;
37 VbaInputStream::VbaInputStream( BinaryInputStream
& rInStrm
) :
38 BinaryStreamBase( false ),
42 maChunk
.reserve( 4096 );
44 sal_uInt8 nSig
= rInStrm
.readuInt8();
45 OSL_ENSURE( nSig
== VBASTREAM_SIGNATURE
, "VbaInputStream::VbaInputStream - wrong signature" );
46 mbEof
= mbEof
|| rInStrm
.isEof() || (nSig
!= VBASTREAM_SIGNATURE
);
49 sal_Int64
VbaInputStream::size() const
54 sal_Int64
VbaInputStream::tell() const
59 void VbaInputStream::seek( sal_Int64
)
63 void VbaInputStream::close()
69 sal_Int32
VbaInputStream::readData( StreamDataSequence
& orData
, sal_Int32 nBytes
, size_t nAtomSize
)
74 orData
.realloc( ::std::max
< sal_Int32
>( nBytes
, 0 ) );
77 nRet
= readMemory( orData
.getArray(), nBytes
, nAtomSize
);
79 orData
.realloc( nRet
);
85 sal_Int32
VbaInputStream::readMemory( void* opMem
, sal_Int32 nBytes
, size_t /*nAtomSize*/ )
88 sal_uInt8
* opnMem
= static_cast< sal_uInt8
* >( opMem
);
89 while( (nBytes
> 0) && updateChunk() )
91 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
92 sal_Int32 nReadBytes
= ::std::min( nBytes
, nChunkLeft
);
93 memcpy( opnMem
, &*(maChunk
.begin() + mnChunkPos
), nReadBytes
);
95 mnChunkPos
+= static_cast< size_t >( nReadBytes
);
102 void VbaInputStream::skip( sal_Int32 nBytes
, size_t /*nAtomSize*/ )
104 while( (nBytes
> 0) && updateChunk() )
106 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
107 sal_Int32 nSkipBytes
= ::std::min( nBytes
, nChunkLeft
);
108 mnChunkPos
+= static_cast< size_t >( nSkipBytes
);
109 nBytes
-= nSkipBytes
;
113 // private --------------------------------------------------------------------
115 bool VbaInputStream::updateChunk()
117 if( mbEof
|| (mnChunkPos
< maChunk
.size()) ) return !mbEof
;
118 // try to read next chunk header, this may trigger EOF
119 sal_uInt16 nHeader
= mpInStrm
->readuInt16();
121 mbEof
= mpInStrm
->isEof();
122 if( mbEof
) return false;
124 // check header signature
125 bool bIgnoreBrokenSig
= !( (nHeader
& VBACHUNK_SIGMASK
) == VBACHUNK_SIG
);
127 // decode length of chunk data and compression flag
128 bool bCompressed
= getFlag( nHeader
, VBACHUNK_COMPRESSED
);
129 sal_uInt16 nChunkLen
= (nHeader
& VBACHUNK_LENMASK
) + 1;
130 OSL_ENSURE( bCompressed
|| (nChunkLen
== 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" );
132 // From the amazing bit detective work of Valek Filippov<frob@gnome.org>
133 // this tweak and the one at the bottom of the method to seek to the
134 // start of the next chunk we can read those strange broken
135 // ( I guess from a MSO bug ) commpessed streams > 4k
137 if ( bIgnoreBrokenSig
)
143 sal_Int64 target
= mpInStrm
->tell() + nChunkLen
;
147 sal_uInt8 nBitCount
= 4;
148 sal_uInt16 nChunkPos
= 0;
149 while( !mbEof
&& !mpInStrm
->isEof() && (nChunkPos
< nChunkLen
) )
151 sal_uInt8 nTokenFlags
= mpInStrm
->readuInt8();
153 for( int nBit
= 0; !mbEof
&& !mpInStrm
->isEof() && (nBit
< 8) && (nChunkPos
< nChunkLen
); ++nBit
, nTokenFlags
>>= 1 )
155 if( nTokenFlags
& 1 )
157 sal_uInt16 nCopyToken
= mpInStrm
->readuInt16();
158 nChunkPos
= nChunkPos
+ 2;
159 // update bit count used for offset/length in the token
160 while( static_cast< size_t >( 1 << nBitCount
) < maChunk
.size() ) ++nBitCount
;
161 // extract length from lower (16-nBitCount) bits, plus 3
162 sal_uInt16 nLength
= extractValue
< sal_uInt16
>( nCopyToken
, 0, 16 - nBitCount
) + 3;
163 // extract offset from high nBitCount bits, plus 1
164 sal_uInt16 nOffset
= extractValue
< sal_uInt16
>( nCopyToken
, 16 - nBitCount
, nBitCount
) + 1;
165 mbEof
= (nOffset
> maChunk
.size()) || (maChunk
.size() + nLength
> 4096);
166 OSL_ENSURE( !mbEof
, "VbaInputStream::updateChunk - invalid offset or size in copy token" );
169 // append data to buffer
170 maChunk
.resize( maChunk
.size() + nLength
);
171 sal_uInt8
* pnTo
= &*(maChunk
.end() - nLength
);
172 const sal_uInt8
* pnEnd
= pnTo
+ nLength
;
173 const sal_uInt8
* pnFrom
= pnTo
- nOffset
;
174 // offset may be less than length, effectively duplicating source data several times
175 size_t nRunLen
= ::std::min
< size_t >( nLength
, nOffset
);
176 while( pnTo
< pnEnd
)
178 size_t nStepLen
= ::std::min
< size_t >( nRunLen
, pnEnd
- pnTo
);
179 memcpy( pnTo
, pnFrom
, nStepLen
);
184 // we suspect this will never be called
187 maChunk
.resize( maChunk
.size() + 1 );
188 maChunk
.back() = mpInStrm
->readuChar();
196 maChunk
.resize( nChunkLen
);
197 mpInStrm
->readMemory( &maChunk
.front(), nChunkLen
);
199 // decompression sometimes leaves the stream pos offset 1 place ( at
200 // least ) past or before the expected stream pos.
201 // here we make sure we are on the chunk boundary
202 mpInStrm
->seek( target
);
210 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */