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>
27 const sal_uInt8 VBASTREAM_SIGNATURE
= 1;
29 const sal_uInt16 VBACHUNK_SIGMASK
= 0x7000;
30 const sal_uInt16 VBACHUNK_SIG
= 0x3000;
31 const sal_uInt16 VBACHUNK_COMPRESSED
= 0x8000;
32 const sal_uInt16 VBACHUNK_LENMASK
= 0x0FFF;
36 VbaInputStream::VbaInputStream( BinaryInputStream
& rInStrm
) :
37 BinaryStreamBase( false ),
41 maChunk
.reserve( 4096 );
43 sal_uInt8 nSig
= rInStrm
.readuInt8();
44 OSL_ENSURE( nSig
== VBASTREAM_SIGNATURE
, "VbaInputStream::VbaInputStream - wrong signature" );
45 mbEof
= mbEof
|| rInStrm
.isEof() || (nSig
!= VBASTREAM_SIGNATURE
);
48 sal_Int64
VbaInputStream::size() const
53 sal_Int64
VbaInputStream::tell() const
58 void VbaInputStream::seek( sal_Int64
)
62 void VbaInputStream::close()
68 sal_Int32
VbaInputStream::readData( StreamDataSequence
& orData
, sal_Int32 nBytes
, size_t nAtomSize
)
73 orData
.realloc( ::std::max
< sal_Int32
>( nBytes
, 0 ) );
76 nRet
= readMemory( orData
.getArray(), nBytes
, nAtomSize
);
78 orData
.realloc( nRet
);
84 sal_Int32
VbaInputStream::readMemory( void* opMem
, sal_Int32 nBytes
, size_t /*nAtomSize*/ )
87 sal_uInt8
* opnMem
= static_cast< sal_uInt8
* >( opMem
);
88 while( (nBytes
> 0) && updateChunk() )
90 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
91 sal_Int32 nReadBytes
= ::std::min( nBytes
, nChunkLeft
);
92 memcpy( opnMem
, &*(maChunk
.begin() + mnChunkPos
), nReadBytes
);
94 mnChunkPos
+= static_cast< size_t >( nReadBytes
);
101 void VbaInputStream::skip( sal_Int32 nBytes
, size_t /*nAtomSize*/ )
103 while( (nBytes
> 0) && updateChunk() )
105 sal_Int32 nChunkLeft
= static_cast< sal_Int32
>( maChunk
.size() - mnChunkPos
);
106 sal_Int32 nSkipBytes
= ::std::min( nBytes
, nChunkLeft
);
107 mnChunkPos
+= static_cast< size_t >( nSkipBytes
);
108 nBytes
-= nSkipBytes
;
112 // private --------------------------------------------------------------------
114 bool VbaInputStream::updateChunk()
116 if( mbEof
|| (mnChunkPos
< maChunk
.size()) ) return !mbEof
;
117 // try to read next chunk header, this may trigger EOF
118 sal_uInt16 nHeader
= mpInStrm
->readuInt16();
120 mbEof
= mpInStrm
->isEof();
121 if( mbEof
) return false;
123 // check header signature
124 bool bIgnoreBrokenSig
= ( (nHeader
& VBACHUNK_SIGMASK
) != VBACHUNK_SIG
);
126 // decode length of chunk data and compression flag
127 bool bCompressed
= getFlag( nHeader
, VBACHUNK_COMPRESSED
);
128 sal_uInt16 nChunkLen
= (nHeader
& VBACHUNK_LENMASK
) + 1;
129 OSL_ENSURE( bCompressed
|| (nChunkLen
== 4096), "VbaInputStream::updateChunk - invalid uncompressed chunk size" );
131 // From the amazing bit detective work of Valek Filippov<frob@gnome.org>
132 // this tweak and the one at the bottom of the method to seek to the
133 // start of the next chunk we can read those strange broken
134 // ( I guess from a MSO bug ) compressed streams > 4k
136 if ( bIgnoreBrokenSig
)
142 sal_Int64 target
= mpInStrm
->tell() + nChunkLen
;
146 sal_uInt8 nBitCount
= 4;
147 sal_uInt16 nChunkPos
= 0;
148 while( !mbEof
&& !mpInStrm
->isEof() && (nChunkPos
< nChunkLen
) )
150 sal_uInt8 nTokenFlags
= mpInStrm
->readuInt8();
152 for( int nBit
= 0; !mbEof
&& !mpInStrm
->isEof() && (nBit
< 8) && (nChunkPos
< nChunkLen
); ++nBit
, nTokenFlags
>>= 1 )
154 if( nTokenFlags
& 1 )
156 sal_uInt16 nCopyToken
= mpInStrm
->readuInt16();
157 nChunkPos
= nChunkPos
+ 2;
158 // update bit count used for offset/length in the token
159 while( ( static_cast<size_t>(1) << nBitCount
) < maChunk
.size() ) ++nBitCount
;
160 // extract length from lower (16-nBitCount) bits, plus 3
161 sal_uInt16 nLength
= extractValue
< sal_uInt16
>( nCopyToken
, 0, 16 - nBitCount
) + 3;
162 // extract offset from high nBitCount bits, plus 1
163 sal_uInt16 nOffset
= extractValue
< sal_uInt16
>( nCopyToken
, 16 - nBitCount
, nBitCount
) + 1;
164 mbEof
= (nOffset
> maChunk
.size()) || (maChunk
.size() + nLength
> 4096);
165 OSL_ENSURE( !mbEof
, "VbaInputStream::updateChunk - invalid offset or size in copy token" );
168 // append data to buffer
169 maChunk
.resize( maChunk
.size() + nLength
);
170 sal_uInt8
* pnTo
= &*(maChunk
.end() - nLength
);
171 const sal_uInt8
* pnEnd
= pnTo
+ nLength
;
172 const sal_uInt8
* pnFrom
= pnTo
- nOffset
;
173 // offset may be less than length, effectively duplicating source data several times
174 size_t nRunLen
= ::std::min
< size_t >( nLength
, nOffset
);
175 while( pnTo
< pnEnd
)
177 size_t nStepLen
= ::std::min
< size_t >( nRunLen
, pnEnd
- pnTo
);
178 memcpy( pnTo
, pnFrom
, nStepLen
);
183 // we suspect this will never be called
186 maChunk
.emplace_back();
187 maChunk
.back() = mpInStrm
->readuChar();
195 maChunk
.resize( nChunkLen
);
196 mpInStrm
->readMemory(maChunk
.data(), nChunkLen
);
198 // decompression sometimes leaves the stream pos offset 1 place ( at
199 // least ) past or before the expected stream pos.
200 // here we make sure we are on the chunk boundary
201 mpInStrm
->seek( target
);
206 } // namespace oox::ole
208 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */