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 <tools/stream.hxx>
21 #include <tools/tenccvt.hxx>
22 #include <osl/thread.h>
23 #include <sal/log.hxx>
24 #include <basic/sbx.hxx>
26 #include <sbxprop.hxx>
29 #include <codegen.hxx>
34 nFlags
= SbiImageFlags::NONE
;
42 eCharSet
= osl_getThreadTextEncoding();
51 void SbiImage::Clear()
53 mvStringOffsets
.clear();
57 nFlags
= SbiImageFlags::NONE
;
61 eCharSet
= osl_getThreadTextEncoding();
66 static bool SbiGood( SvStream
const & r
)
72 static sal_uInt64
SbiOpenRecord( SvStream
& r
, FileOffset nSignature
, sal_uInt16 nElem
)
74 sal_uInt64 nPos
= r
.Tell();
75 r
.WriteUInt16( static_cast<sal_uInt16
>( nSignature
) )
76 .WriteInt32( 0 ).WriteUInt16( nElem
);
81 static void SbiCloseRecord( SvStream
& r
, sal_uInt64 nOff
)
83 sal_uInt64 nPos
= r
.Tell();
85 r
.WriteInt32(nPos
- nOff
- 8 );
89 constexpr sal_uInt32 nUnicodeDataMagicNumber
= 0x556E6920; // "Uni " BE
91 static bool GetToUnicodePoolData(SvStream
& r
, sal_uInt64 nLen
, sal_uInt64 nNext
)
93 const auto nPos
= r
.Tell();
94 // Check space for legacy data, magic number and Unicode data
95 bool bResult
= nPos
+ nLen
+ sizeof(sal_uInt32
) + nLen
* sizeof(sal_Unicode
) <= nNext
;
98 r
.SeekRel(nLen
); // Skip legacy data
99 sal_uInt32 nMagic
= 0;
100 r
.ReadUInt32(nMagic
);
101 if (nMagic
!= nUnicodeDataMagicNumber
)
103 r
.Seek(nPos
); // return
110 bool SbiImage::Load( SvStream
& r
, sal_uInt32
& nVersion
)
113 sal_uInt16 nSign
, nCount
;
114 sal_uInt32 nLen
, nOff
;
117 // Read Master-Record
118 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
119 sal_uInt64 nLast
= r
.Tell() + nLen
;
120 bool bBadVer
= false;
121 if( nSign
== static_cast<sal_uInt16
>( FileOffset::Module
) )
123 sal_uInt32 nCharSet
; // System charset
125 sal_uInt16 nTmpFlags
;
126 sal_uInt16 nReserved1
;
127 sal_uInt32 nReserved2
;
128 sal_uInt32 nReserved3
;
129 r
.ReadUInt32( nVersion
).ReadUInt32( nCharSet
).ReadUInt32( lDimBase
)
130 .ReadUInt16( nTmpFlags
).ReadUInt16( nReserved1
).ReadUInt32( nReserved2
).ReadUInt32( nReserved3
);
131 nFlags
= static_cast<SbiImageFlags
>(nTmpFlags
);
133 eCharSet
= GetSOLoadTextEncoding( eCharSet
);
134 bBadVer
= ( nVersion
> B_CURVERSION
);
135 nDimBase
= static_cast<sal_uInt16
>(lDimBase
);
138 bool bLegacy
= ( nVersion
< B_EXT_IMG_VERSION
);
141 while( ( nNext
= r
.Tell() ) < nLast
)
144 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
146 if( r
.GetError() == ERRCODE_NONE
)
148 switch( static_cast<FileOffset
>( nSign
) )
150 case FileOffset::Name
:
151 aName
= r
.ReadUniOrByteString(eCharSet
);
153 case FileOffset::Comment
:
154 aComment
= r
.ReadUniOrByteString(eCharSet
);
156 case FileOffset::Source
:
158 aOUSource
= r
.ReadUniOrByteString(eCharSet
);
161 case FileOffset::ExtSource
:
163 //assuming an empty string with just the lead 32bit/16bit len indicator
164 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
165 const sal_uInt64 nMaxStrings
= r
.remainingSize() / nMinStringSize
;
166 if (nCount
> nMaxStrings
)
168 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
169 " max possible entries, but " << nCount
<< " claimed, truncating");
170 nCount
= nMaxStrings
;
172 for( sal_uInt16 j
= 0; j
< nCount
; ++j
)
174 aOUSource
+= r
.ReadUniOrByteString(eCharSet
);
178 case FileOffset::PCode
:
180 pCode
.reset(new char[ nLen
]);
182 r
.ReadBytes(pCode
.get(), nCodeSize
);
185 nLegacyCodeSize
= static_cast<sal_uInt16
>(nCodeSize
);
186 pLegacyPCode
= std::move(pCode
);
188 PCodeBuffConvertor
< sal_uInt16
, sal_uInt32
> aLegacyToNew( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
.get()), nLegacyCodeSize
);
189 aLegacyToNew
.convert();
190 pCode
.reset(reinterpret_cast<char*>(aLegacyToNew
.GetBuffer()));
191 nCodeSize
= aLegacyToNew
.GetSize();
192 // we don't release the legacy buffer
193 // right now, that's because the module
194 // needs it to fix up the method
195 // nStart members. When that is done
196 // the module can release the buffer
197 // or it can wait until this routine
198 // is called again or when this class // destructs all of which will trigger
199 // release of the buffer.
202 case FileOffset::Publics
:
203 case FileOffset::PoolDir
:
204 case FileOffset::SymPool
:
205 case FileOffset::LineRanges
:
207 case FileOffset::StringPool
:
209 // the data layout is: nCount of 32-bit offsets into both legacy 1-byte char stream
210 // and resulting char buffer (1:1 correspondence assumed; 16 of 32 bits used);
211 // 32-bit length N of following 1-byte char stream (16 bits used); N bytes of 1-byte
212 // char stream; then optional magic number and stream of N sal_Unicode characters.
215 //assuming an empty string with just the lead 32bit len indicator
216 const sal_uInt64 nMinStringSize
= 4;
217 const sal_uInt64 nMaxStrings
= r
.remainingSize() / nMinStringSize
;
218 if (nCount
> nMaxStrings
)
220 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
221 " max possible entries, but " << nCount
<< " claimed, truncating");
222 nCount
= nMaxStrings
;
224 MakeStrings( nCount
);
225 for( size_t i
= 0; i
< mvStringOffsets
.size() && SbiGood( r
); i
++ )
227 r
.ReadUInt32( nOff
);
228 mvStringOffsets
[ i
] = static_cast<sal_uInt16
>(nOff
);
230 r
.ReadUInt32( nLen
);
233 pStrings
.reset(new sal_Unicode
[ nLen
]);
234 nStringSize
= static_cast<sal_uInt16
>(nLen
);
236 if (GetToUnicodePoolData(r
, nLen
, nNext
))
238 OUString s
= read_uInt16s_ToOUString(r
, nLen
);
239 memcpy(pStrings
.get(), s
.getStr(), s
.getLength() * sizeof(sal_Unicode
));
243 std::unique_ptr
<char[]> pByteStrings(new char[nLen
]);
244 r
.ReadBytes(pByteStrings
.get(), nLen
);
245 for (size_t j
= 0; j
< mvStringOffsets
.size(); j
++)
247 sal_uInt16 nOff2
= static_cast<sal_uInt16
>(mvStringOffsets
[j
]);
248 OUString
aStr(pByteStrings
.get() + nOff2
, strlen(pByteStrings
.get() + nOff2
), eCharSet
);
249 memcpy(pStrings
.get() + nOff2
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof(sal_Unicode
));
255 case FileOffset::UserTypes
:
257 //assuming an empty string with just the lead 32bit/16bit len indicator
258 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
259 const sal_uInt64 nMinRecordSize
= nMinStringSize
+ sizeof(sal_Int16
);
260 const sal_uInt64 nMaxRecords
= r
.remainingSize() / nMinRecordSize
;
261 if (nCount
> nMaxRecords
)
263 SAL_WARN("basic", "Parsing error: " << nMaxRecords
<<
264 " max possible entries, but " << nCount
<< " claimed, truncating");
265 nCount
= nMaxRecords
;
268 // User defined types; ref.: SbiParser::DefType
269 for (sal_uInt16 i
= 0; i
< nCount
; i
++)
271 OUString aTypeName
= r
.ReadUniOrByteString(eCharSet
);
273 sal_uInt16 nTypeMembers
;
274 r
.ReadUInt16(nTypeMembers
);
276 const sal_uInt64 nMaxTypeMembers
= r
.remainingSize() / 8;
277 if (nTypeMembers
> nMaxTypeMembers
)
279 SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers
<<
280 " max possible entries, but " << nTypeMembers
<< " claimed, truncating");
281 nTypeMembers
= nMaxTypeMembers
;
284 SbxObject
*pType
= new SbxObject(aTypeName
);
285 SbxArray
*pTypeMembers
= pType
->GetProperties();
287 for (sal_uInt16 j
= 0; j
< nTypeMembers
; j
++)
289 OUString aMemberName
= r
.ReadUniOrByteString(eCharSet
);
291 sal_Int16 aIntMemberType
;
292 r
.ReadInt16(aIntMemberType
);
293 SbxDataType aMemberType
= static_cast< SbxDataType
> ( aIntMemberType
);
295 SbxProperty
*pTypeElem
= new SbxProperty( aMemberName
, aMemberType
);
298 r
.ReadUInt32(aIntFlag
);
299 SbxFlagBits nElemFlags
= static_cast< SbxFlagBits
> ( aIntFlag
);
301 pTypeElem
->SetFlags(nElemFlags
);
304 r
.ReadInt16(hasObject
);
308 if(aMemberType
== SbxOBJECT
)
310 // nested user defined types
311 // declared before use, so it is ok to reference it by name on load
312 OUString aNestedTypeName
= r
.ReadUniOrByteString(eCharSet
);
313 SbxObject
* pNestedTypeObj
= static_cast< SbxObject
* >( rTypes
->Find( aNestedTypeName
, SbxClassType::Object
) );
316 SbxObject
* pCloneObj
= cloneTypeObjectImpl( *pNestedTypeObj
);
317 pTypeElem
->PutObject( pCloneObj
);
323 SbxDimArray
* pArray
= new SbxDimArray(
324 static_cast<SbxDataType
>(aMemberType
& 0x0FFF));
326 sal_Int16 isFixedSize
;
327 r
.ReadInt16(isFixedSize
);
328 if (isFixedSize
== 1)
329 pArray
->setHasFixedSize( true );
333 for (sal_Int32 d
= 0; d
< nDims
; d
++)
337 r
.ReadInt32(lBound
).ReadInt32(uBound
);
338 pArray
->unoAddDim32(lBound
, uBound
);
341 const SbxFlagBits nSavFlags
= pTypeElem
->GetFlags();
342 // need to reset the FIXED flag
343 // when calling PutObject ( because the type will not match Object )
344 pTypeElem
->ResetFlag(SbxFlagBits::Fixed
);
345 pTypeElem
->PutObject( pArray
);
346 pTypeElem
->SetFlags(nSavFlags
);
350 pTypeMembers
->Insert32( pTypeElem
, pTypeMembers
->Count32() );
354 pType
->Remove( "Name", SbxClassType::DontCare
);
355 pType
->Remove( "Parent", SbxClassType::DontCare
);
361 case FileOffset::ModEnd
:
382 bool SbiImage::Save( SvStream
& r
, sal_uInt32 nVer
)
384 bool bLegacy
= ( nVer
< B_EXT_IMG_VERSION
);
386 // detect if old code exceeds legacy limits
387 // if so, then disallow save
388 if ( bLegacy
&& ExceedsLegacyLimits() )
391 aEmptyImg
.aName
= aName
;
392 aEmptyImg
.Save( r
, B_LEGACYVERSION
);
395 // First of all the header
396 sal_uInt64 nStart
= SbiOpenRecord( r
, FileOffset::Module
, 1 );
399 eCharSet
= GetSOStoreTextEncoding( eCharSet
);
402 r
.WriteInt32( B_LEGACYVERSION
);
406 r
.WriteInt32( B_CURVERSION
);
408 r
.WriteInt32( eCharSet
)
409 .WriteInt32( nDimBase
)
410 .WriteInt16( static_cast<sal_uInt16
>(nFlags
) )
416 if( !aName
.isEmpty() && SbiGood( r
) )
418 nPos
= SbiOpenRecord( r
, FileOffset::Name
, 1 );
419 r
.WriteUniOrByteString( aName
, eCharSet
);
420 SbiCloseRecord( r
, nPos
);
423 if( !aComment
.isEmpty() && SbiGood( r
) )
425 nPos
= SbiOpenRecord( r
, FileOffset::Comment
, 1 );
426 r
.WriteUniOrByteString( aComment
, eCharSet
);
427 SbiCloseRecord( r
, nPos
);
430 if( !aOUSource
.isEmpty() && SbiGood( r
) )
432 nPos
= SbiOpenRecord( r
, FileOffset::Source
, 1 );
433 r
.WriteUniOrByteString( aOUSource
, eCharSet
);
434 SbiCloseRecord( r
, nPos
);
437 if( pCode
&& SbiGood( r
) )
439 nPos
= SbiOpenRecord( r
, FileOffset::PCode
, 1 );
442 PCodeBuffConvertor
< sal_uInt32
, sal_uInt16
> aNewToLegacy( reinterpret_cast<sal_uInt8
*>(pCode
.get()), nCodeSize
);
443 aNewToLegacy
.convert();
444 pLegacyPCode
.reset(reinterpret_cast<char*>(aNewToLegacy
.GetBuffer()));
445 nLegacyCodeSize
= aNewToLegacy
.GetSize();
446 r
.WriteBytes(pLegacyPCode
.get(), nLegacyCodeSize
);
450 r
.WriteBytes(pCode
.get(), nCodeSize
);
452 SbiCloseRecord( r
, nPos
);
455 if( !mvStringOffsets
.empty() )
457 nPos
= SbiOpenRecord( r
, FileOffset::StringPool
, mvStringOffsets
.size() );
459 // sal_uInt32 Offset of the Strings in the Stringblock
460 for( size_t i
= 0; i
< mvStringOffsets
.size() && SbiGood( r
); i
++ )
462 r
.WriteUInt32( mvStringOffsets
[ i
] );
464 // Then the String-Block
465 std::unique_ptr
<char[]> pByteStrings(new char[ nStringSize
]);
466 for( size_t i
= 0; i
< mvStringOffsets
.size(); i
++ )
468 sal_uInt16 nOff
= static_cast<sal_uInt16
>(mvStringOffsets
[ i
]);
469 OString
aStr(OUStringToOString(OUString(pStrings
.get() + nOff
), eCharSet
));
470 memcpy( pByteStrings
.get() + nOff
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof( char ) );
472 r
.WriteUInt32( nStringSize
);
473 r
.WriteBytes(pByteStrings
.get(), nStringSize
);
474 pByteStrings
.reset();
476 // Now write magic number and store the same data in UTF-16; this is backward compatible:
477 // old readers will not read this data after having read legacy data, and will proceed
478 // straight to the end of the record. So no version restriction here.
479 r
.WriteUInt32(nUnicodeDataMagicNumber
);
480 write_uInt16s_FromOUString(r
, OUString(pStrings
.get(), nStringSize
));
482 SbiCloseRecord( r
, nPos
);
484 // User defined types
487 sal_uInt32 nTypes
= rTypes
->Count32();
488 assert(nTypes
<= std::numeric_limits
<sal_uInt16
>::max());
491 nPos
= SbiOpenRecord( r
, FileOffset::UserTypes
, sal::static_int_cast
<sal_uInt16
>(nTypes
) );
493 for (sal_uInt32 i
= 0; i
< nTypes
; i
++)
495 SbxObject
* pType
= static_cast< SbxObject
* > ( rTypes
->Get32(i
) );
496 OUString aTypeName
= pType
->GetClassName();
498 r
.WriteUniOrByteString( aTypeName
, eCharSet
);
500 SbxArray
*pTypeMembers
= pType
->GetProperties();
501 sal_uInt32 nTypeMembers
= pTypeMembers
->Count32();
502 assert(nTypeMembers
<= std::numeric_limits
<sal_uInt16
>::max());
504 r
.WriteInt16(sal::static_int_cast
<sal_uInt16
>(nTypeMembers
));
506 for (sal_uInt32 j
= 0; j
< nTypeMembers
; j
++)
509 SbxProperty
* pTypeElem
= static_cast< SbxProperty
* > ( pTypeMembers
->Get32(j
) );
511 const OUString
& aElemName
= pTypeElem
->GetName();
512 r
.WriteUniOrByteString( aElemName
, eCharSet
);
514 SbxDataType dataType
= pTypeElem
->GetType();
515 r
.WriteInt16(dataType
);
517 SbxFlagBits nElemFlags
= pTypeElem
->GetFlags();
518 r
.WriteUInt32(static_cast< sal_uInt32
> (nElemFlags
) );
520 SbxBase
* pElemObject
= pTypeElem
->GetObject();
524 r
.WriteInt16(1); // has elem Object
526 if( dataType
== SbxOBJECT
)
528 // nested user defined types
529 // declared before use, so it is ok to reference it by name on load
530 SbxObject
* pNestedType
= static_cast< SbxObject
* > ( pElemObject
);
531 r
.WriteUniOrByteString( pNestedType
->GetClassName(), eCharSet
);
536 SbxDimArray
* pArray
= static_cast< SbxDimArray
* > ( pElemObject
);
538 bool bFixedSize
= pArray
->hasFixedSize();
544 sal_Int32 nDims
= pArray
->GetDims32();
547 for (sal_Int32 d
= 1; d
<= nDims
; d
++)
551 pArray
->GetDim32(d
, lBound
, uBound
);
552 r
.WriteInt32(lBound
).WriteInt32(uBound
);
557 r
.WriteInt16(0); // no elem Object
561 SbiCloseRecord( r
, nPos
);
564 // Set overall length
565 SbiCloseRecord( r
, nStart
);
573 void SbiImage::MakeStrings( short nSize
)
578 pStrings
.reset( new sal_Unicode
[ nStringSize
]);
579 mvStringOffsets
.resize(nSize
);
581 memset( mvStringOffsets
.data(), 0, nSize
* sizeof( sal_uInt32
) );
583 memset( pStrings
.get(), 0, nStringSize
* sizeof( sal_Unicode
) );
586 // Add a string to StringPool. The String buffer is dynamically
587 // growing in 1K-Steps
588 void SbiImage::AddString( const OUString
& r
)
590 if( nStringIdx
>= short(mvStringOffsets
.size()) )
597 sal_Int32 len
= r
.getLength() + 1;
598 sal_uInt32 needed
= nStringOff
+ len
;
599 if( needed
> 0xFFFFFF00 )
601 bError
= true; // out of mem!
603 else if( needed
> nStringSize
)
605 sal_uInt32 nNewLen
= needed
+ 1024;
606 nNewLen
&= 0xFFFFFC00; // trim to 1K border
607 std::unique_ptr
<sal_Unicode
[]> p(new sal_Unicode
[nNewLen
]);
608 memcpy( p
.get(), pStrings
.get(), nStringSize
* sizeof( sal_Unicode
) );
609 pStrings
= std::move(p
);
610 nStringSize
= sal::static_int_cast
< sal_uInt16
>(nNewLen
);
614 mvStringOffsets
[ nStringIdx
++ ] = nStringOff
;
615 memcpy( pStrings
.get() + nStringOff
, r
.getStr(), len
* sizeof( sal_Unicode
) );
616 nStringOff
= nStringOff
+ len
;
617 // Last String? The update the size of the buffer
618 if( nStringIdx
>= short(mvStringOffsets
.size()) )
620 nStringSize
= nStringOff
;
626 // The block was fetched by the compiler from class SbBuffer and
627 // is already created with new. Additionally it contains all Integers
628 // in Big Endian format, so can be directly read/written.
629 void SbiImage::AddCode( std::unique_ptr
<char[]> p
, sal_uInt32 s
)
631 pCode
= std::move(p
);
636 void SbiImage::AddType(SbxObject
const * pObject
)
640 rTypes
= new SbxArray
;
642 SbxObject
*pCopyObject
= new SbxObject(*pObject
);
643 rTypes
->Insert32 (pCopyObject
,rTypes
->Count32());
646 void SbiImage::AddEnum(SbxObject
* pObject
) // Register enum type
650 rEnums
= new SbxArray
;
652 rEnums
->Insert32( pObject
, rEnums
->Count32() );
655 // Note: IDs start with 1
656 OUString
SbiImage::GetString( short nId
) const
658 if( nId
&& nId
<= short(mvStringOffsets
.size()) )
660 sal_uInt32 nOff
= mvStringOffsets
[ nId
- 1 ];
661 sal_Unicode
* pStr
= pStrings
.get() + nOff
;
663 // #i42467: Special treatment for vbNullChar
666 sal_uInt32 nNextOff
= (nId
< short(mvStringOffsets
.size())) ? mvStringOffsets
[ nId
] : nStringOff
;
667 sal_uInt32 nLen
= nNextOff
- nOff
- 1;
670 return OUString( u
'\0');
675 return OUString(pStr
);
681 const SbxObject
* SbiImage::FindType (const OUString
& aTypeName
) const
683 return rTypes
.is() ? static_cast<SbxObject
*>(rTypes
->Find(aTypeName
,SbxClassType::Object
)) : nullptr;
686 sal_uInt16
SbiImage::CalcLegacyOffset( sal_Int32 nOffset
)
688 return SbiCodeGen::calcLegacyOffSet( reinterpret_cast<sal_uInt8
*>(pCode
.get()), nOffset
) ;
691 sal_uInt32
SbiImage::CalcNewOffset( sal_Int16 nOffset
)
693 return SbiCodeGen::calcNewOffSet( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
.get()), nOffset
) ;
696 void SbiImage::ReleaseLegacyBuffer()
698 pLegacyPCode
.reset();
702 bool SbiImage::ExceedsLegacyLimits()
704 return ( nStringSize
> 0xFF00 ) || ( CalcLegacyOffset( nCodeSize
) > 0xFF00 );
707 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */