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>
26 // ============================================================================
30 const sal_uInt8 VBASTREAM_SIGNATURE
= 1;
32 const sal_uInt16 VBACHUNK_SIGMASK
= 0x7000;
33 const sal_uInt16 VBACHUNK_SIG
= 0x3000;
34 const sal_uInt16 VBACHUNK_COMPRESSED
= 0x8000;
35 const sal_uInt16 VBACHUNK_LENMASK
= 0x0FFF;
39 // ============================================================================
41 VbaInputStream::VbaInputStream( BinaryInputStream
& rInStrm
) :
42 BinaryStreamBase( false ),
46 maChunk
.reserve( 4096 );
48 sal_uInt8 nSig
= rInStrm
.readuInt8();
49 OSL_ENSURE( nSig
== VBASTREAM_SIGNATURE
, "VbaInputStream::VbaInputStream - wrong signature" );
50 mbEof
= mbEof
|| rInStrm
.isEof() || (nSig
!= VBASTREAM_SIGNATURE
);
53 sal_Int64
VbaInputStream::size() const
58 sal_Int64
VbaInputStream::tell() const
63 void VbaInputStream::seek( sal_Int64
)
67 void VbaInputStream::close()
73 sal_Int32
VbaInputStream::readData( StreamDataSequence
& orData
, sal_Int32 nBytes
, size_t nAtomSize
)
78 orData
.realloc( ::std::max
< sal_Int32
>( nBytes
, 0 ) );
81 nRet
= readMemory( orData
.getArray(), nBytes
, nAtomSize
);
83 orData
.realloc( nRet
);
89 sal_Int32
VbaInputStream::readMemory( void* opMem
, sal_Int32 nBytes
, size_t /*nAtomSize*/ )
92 sal_uInt8
* opnMem
= reinterpret_cast< sal_uInt8
* >( opMem
);
93 while( (nBytes
> 0) && updateChunk() )
95 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
96 sal_Int32 nReadBytes
= ::std::min( nBytes
, nChunkLeft
);
97 memcpy( opnMem
, &*(maChunk
.begin() + mnChunkPos
), nReadBytes
);
99 mnChunkPos
+= static_cast< size_t >( nReadBytes
);
100 nBytes
-= nReadBytes
;
106 void VbaInputStream::skip( sal_Int32 nBytes
, size_t /*nAtomSize*/ )
108 while( (nBytes
> 0) && updateChunk() )
110 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
111 sal_Int32 nSkipBytes
= ::std::min( nBytes
, nChunkLeft
);
112 mnChunkPos
+= static_cast< size_t >( nSkipBytes
);
113 nBytes
-= nSkipBytes
;
117 // private --------------------------------------------------------------------
119 bool VbaInputStream::updateChunk()
121 if( mbEof
|| (mnChunkPos
< maChunk
.size()) ) return !mbEof
;
122 // try to read next chunk header, this may trigger EOF
123 sal_uInt16 nHeader
= mpInStrm
->readuInt16();
125 mbEof
= mpInStrm
->isEof();
126 if( mbEof
) return false;
128 // check header signature
129 bool bIgnoreBrokenSig
= !( (nHeader
& VBACHUNK_SIGMASK
) == VBACHUNK_SIG
);
131 // decode length of chunk data and compression flag
132 bool bCompressed
= getFlag( nHeader
, VBACHUNK_COMPRESSED
);
133 sal_uInt16 nChunkLen
= (nHeader
& VBACHUNK_LENMASK
) + 1;
134 OSL_ENSURE( bCompressed
|| (nChunkLen
== 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" );
136 // From the amazing bit detective work of Valek Filippov<frob@gnome.org>
137 // this tweak and the one at the bottom of the method to seek to the
138 // start of the next chunk we can read those strange broken
139 // ( I guess from a MSO bug ) commpessed streams > 4k
141 if ( bIgnoreBrokenSig
)
147 sal_Int64 target
= mpInStrm
->tell() + nChunkLen
;
151 sal_uInt8 nBitCount
= 4;
152 sal_uInt16 nChunkPos
= 0;
153 while( !mbEof
&& !mpInStrm
->isEof() && (nChunkPos
< nChunkLen
) )
155 sal_uInt8 nTokenFlags
= mpInStrm
->readuInt8();
157 for( int nBit
= 0; !mbEof
&& !mpInStrm
->isEof() && (nBit
< 8) && (nChunkPos
< nChunkLen
); ++nBit
, nTokenFlags
>>= 1 )
159 if( nTokenFlags
& 1 )
161 sal_uInt16 nCopyToken
= mpInStrm
->readuInt16();
162 nChunkPos
= nChunkPos
+ 2;
163 // update bit count used for offset/length in the token
164 while( static_cast< size_t >( 1 << nBitCount
) < maChunk
.size() ) ++nBitCount
;
165 // extract length from lower (16-nBitCount) bits, plus 3
166 sal_uInt16 nLength
= extractValue
< sal_uInt16
>( nCopyToken
, 0, 16 - nBitCount
) + 3;
167 // extract offset from high nBitCount bits, plus 1
168 sal_uInt16 nOffset
= extractValue
< sal_uInt16
>( nCopyToken
, 16 - nBitCount
, nBitCount
) + 1;
169 mbEof
= (nOffset
> maChunk
.size()) || (maChunk
.size() + nLength
> 4096);
170 OSL_ENSURE( !mbEof
, "VbaInputStream::updateChunk - invalid offset or size in copy token" );
173 // append data to buffer
174 maChunk
.resize( maChunk
.size() + nLength
);
175 sal_uInt8
* pnTo
= &*(maChunk
.end() - nLength
);
176 const sal_uInt8
* pnEnd
= pnTo
+ nLength
;
177 const sal_uInt8
* pnFrom
= pnTo
- nOffset
;
178 // offset may be less than length, effectively duplicating source data several times
179 size_t nRunLen
= ::std::min
< size_t >( nLength
, nOffset
);
180 while( pnTo
< pnEnd
)
182 size_t nStepLen
= ::std::min
< size_t >( nRunLen
, pnEnd
- pnTo
);
183 memcpy( pnTo
, pnFrom
, nStepLen
);
188 // we suspect this will never be called
191 maChunk
.resize( maChunk
.size() + 1 );
192 *mpInStrm
>> maChunk
.back();
200 maChunk
.resize( nChunkLen
);
201 mpInStrm
->readMemory( &maChunk
.front(), nChunkLen
);
203 // decompression sometimes leaves the stream pos offset 1 place ( at
204 // least ) past or before the expected stream pos.
205 // here we make sure we are on the chunk boundry
206 mpInStrm
->seek( target
);
211 // ============================================================================
216 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */