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 <basic/sbxprop.hxx>
29 #include <codegen.hxx>
36 pLegacyPCode
= nullptr;
37 nFlags
= SbiImageFlags::NONE
;
45 eCharSet
= osl_getThreadTextEncoding();
55 void SbiImage::Clear()
57 mvStringOffsets
.clear();
61 nFlags
= SbiImageFlags::NONE
;
65 eCharSet
= osl_getThreadTextEncoding();
70 static bool SbiGood( SvStream
const & r
)
76 static sal_uInt64
SbiOpenRecord( SvStream
& r
, FileOffset nSignature
, sal_uInt16 nElem
)
78 sal_uInt64 nPos
= r
.Tell();
79 r
.WriteUInt16( static_cast<sal_uInt16
>( nSignature
) )
80 .WriteInt32( 0 ).WriteUInt16( nElem
);
85 static void SbiCloseRecord( SvStream
& r
, sal_uInt64 nOff
)
87 sal_uInt64 nPos
= r
.Tell();
89 r
.WriteInt32(nPos
- nOff
- 8 );
93 bool SbiImage::Load( SvStream
& r
, sal_uInt32
& nVersion
)
96 sal_uInt16 nSign
, nCount
;
97 sal_uInt32 nLen
, nOff
;
100 // Read Master-Record
101 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
102 sal_uInt64 nLast
= r
.Tell() + nLen
;
103 sal_uInt32 nCharSet
; // System charset
105 sal_uInt16 nReserved1
;
106 sal_uInt32 nReserved2
;
107 sal_uInt32 nReserved3
;
108 bool bBadVer
= false;
109 if( nSign
== static_cast<sal_uInt16
>( FileOffset::Module
) )
111 sal_uInt16 nTmpFlags
;
112 r
.ReadUInt32( nVersion
).ReadUInt32( nCharSet
).ReadUInt32( lDimBase
)
113 .ReadUInt16( nTmpFlags
).ReadUInt16( nReserved1
).ReadUInt32( nReserved2
).ReadUInt32( nReserved3
);
114 nFlags
= static_cast<SbiImageFlags
>(nTmpFlags
);
116 eCharSet
= GetSOLoadTextEncoding( eCharSet
);
117 bBadVer
= ( nVersion
> B_CURVERSION
);
118 nDimBase
= static_cast<sal_uInt16
>(lDimBase
);
121 bool bLegacy
= ( nVersion
< B_EXT_IMG_VERSION
);
124 while( ( nNext
= r
.Tell() ) < nLast
)
127 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
129 if( r
.GetError() == ERRCODE_NONE
)
131 switch( static_cast<FileOffset
>( nSign
) )
133 case FileOffset::Name
:
134 aName
= r
.ReadUniOrByteString(eCharSet
);
136 case FileOffset::Comment
:
137 aComment
= r
.ReadUniOrByteString(eCharSet
);
139 case FileOffset::Source
:
141 aOUSource
= r
.ReadUniOrByteString(eCharSet
);
144 case FileOffset::ExtSource
:
146 //assuming an empty string with just the lead 32bit/16bit len indicator
147 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
148 const sal_uInt64 nMaxStrings
= r
.remainingSize() / nMinStringSize
;
149 if (nCount
> nMaxStrings
)
151 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
152 " max possible entries, but " << nCount
<< " claimed, truncating");
153 nCount
= nMaxStrings
;
155 for( sal_uInt16 j
= 0; j
< nCount
; ++j
)
157 aOUSource
+= r
.ReadUniOrByteString(eCharSet
);
161 case FileOffset::PCode
:
163 pCode
.reset(new char[ nLen
]);
165 r
.ReadBytes(pCode
.get(), nCodeSize
);
168 nLegacyCodeSize
= static_cast<sal_uInt16
>(nCodeSize
);
169 pLegacyPCode
= std::move(pCode
);
171 PCodeBuffConvertor
< sal_uInt16
, sal_uInt32
> aLegacyToNew( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
.get()), nLegacyCodeSize
);
172 aLegacyToNew
.convert();
173 pCode
.reset(reinterpret_cast<char*>(aLegacyToNew
.GetBuffer()));
174 nCodeSize
= aLegacyToNew
.GetSize();
175 // we don't release the legacy buffer
176 // right now, that's because the module
177 // needs it to fix up the method
178 // nStart members. When that is done
179 // the module can release the buffer
180 // or it can wait until this routine
181 // is called again or when this class // destructs all of which will trigger
182 // release of the buffer.
185 case FileOffset::Publics
:
186 case FileOffset::PoolDir
:
187 case FileOffset::SymPool
:
188 case FileOffset::LineRanges
:
190 case FileOffset::StringPool
:
193 //assuming an empty string with just the lead 32bit len indicator
194 const sal_uInt64 nMinStringSize
= 4;
195 const sal_uInt64 nMaxStrings
= r
.remainingSize() / nMinStringSize
;
196 if (nCount
> nMaxStrings
)
198 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
199 " max possible entries, but " << nCount
<< " claimed, truncating");
200 nCount
= nMaxStrings
;
202 MakeStrings( nCount
);
203 for( size_t i
= 0; i
< mvStringOffsets
.size() && SbiGood( r
); i
++ )
205 r
.ReadUInt32( nOff
);
206 mvStringOffsets
[ i
] = static_cast<sal_uInt16
>(nOff
);
208 r
.ReadUInt32( nLen
);
211 pStrings
.reset(new sal_Unicode
[ nLen
]);
212 nStringSize
= static_cast<sal_uInt16
>(nLen
);
214 std::unique_ptr
<char[]> pByteStrings(new char[ nLen
]);
215 r
.ReadBytes(pByteStrings
.get(), nStringSize
);
216 for( size_t j
= 0; j
< mvStringOffsets
.size(); j
++ )
218 sal_uInt16 nOff2
= static_cast<sal_uInt16
>(mvStringOffsets
[ j
]);
219 OUString
aStr( pByteStrings
.get() + nOff2
, strlen(pByteStrings
.get() + nOff2
), eCharSet
);
220 memcpy( pStrings
.get() + nOff2
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof( sal_Unicode
) );
225 case FileOffset::UserTypes
:
227 //assuming an empty string with just the lead 32bit/16bit len indicator
228 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
229 const sal_uInt64 nMinRecordSize
= nMinStringSize
+ sizeof(sal_Int16
);
230 const sal_uInt64 nMaxRecords
= r
.remainingSize() / nMinRecordSize
;
231 if (nCount
> nMaxRecords
)
233 SAL_WARN("basic", "Parsing error: " << nMaxRecords
<<
234 " max possible entries, but " << nCount
<< " claimed, truncating");
235 nCount
= nMaxRecords
;
238 // User defined types
239 for (sal_uInt16 i
= 0; i
< nCount
; i
++)
241 OUString aTypeName
= r
.ReadUniOrByteString(eCharSet
);
243 sal_uInt16 nTypeMembers
;
244 r
.ReadUInt16(nTypeMembers
);
246 const sal_uInt64 nMaxTypeMembers
= r
.remainingSize() / 8;
247 if (nTypeMembers
> nMaxTypeMembers
)
249 SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers
<<
250 " max possible entries, but " << nTypeMembers
<< " claimed, truncating");
251 nTypeMembers
= nMaxTypeMembers
;
254 SbxObject
*pType
= new SbxObject(aTypeName
);
255 SbxArray
*pTypeMembers
= pType
->GetProperties();
257 for (sal_uInt16 j
= 0; j
< nTypeMembers
; j
++)
259 OUString aMemberName
= r
.ReadUniOrByteString(eCharSet
);
261 sal_Int16 aIntMemberType
;
262 r
.ReadInt16(aIntMemberType
);
263 SbxDataType aMemberType
= static_cast< SbxDataType
> ( aIntMemberType
);
265 SbxProperty
*pTypeElem
= new SbxProperty( aMemberName
, aMemberType
);
268 r
.ReadUInt32(aIntFlag
);
269 SbxFlagBits nElemFlags
= static_cast< SbxFlagBits
> ( aIntFlag
);
271 pTypeElem
->SetFlags(nElemFlags
);
274 r
.ReadInt16(hasObject
);
278 if(aMemberType
== SbxOBJECT
)
280 // nested user defined types
281 // declared before use, so it is ok to reference it by name on load
282 OUString aNestedTypeName
= r
.ReadUniOrByteString(eCharSet
);
283 SbxObject
* pNestedTypeObj
= static_cast< SbxObject
* >( rTypes
->Find( aNestedTypeName
, SbxClassType::Object
) );
286 SbxObject
* pCloneObj
= cloneTypeObjectImpl( *pNestedTypeObj
);
287 pTypeElem
->PutObject( pCloneObj
);
293 SbxDimArray
* pArray
= new SbxDimArray();
295 sal_Int16 isFixedSize
;
296 r
.ReadInt16(isFixedSize
);
297 if (isFixedSize
== 1)
298 pArray
->setHasFixedSize( true );
302 for (sal_Int32 d
= 0; d
< nDims
; d
++)
306 r
.ReadInt32(lBound
).ReadInt32(uBound
);
307 pArray
->unoAddDim32(lBound
, uBound
);
310 pTypeElem
->PutObject( pArray
);
314 pTypeMembers
->Insert( pTypeElem
, pTypeMembers
->Count() );
318 pType
->Remove( "Name", SbxClassType::DontCare
);
319 pType
->Remove( "Parent", SbxClassType::DontCare
);
325 case FileOffset::ModEnd
:
346 bool SbiImage::Save( SvStream
& r
, sal_uInt32 nVer
)
348 bool bLegacy
= ( nVer
< B_EXT_IMG_VERSION
);
350 // detect if old code exceeds legacy limits
351 // if so, then disallow save
352 if ( bLegacy
&& ExceedsLegacyLimits() )
355 aEmptyImg
.aName
= aName
;
356 aEmptyImg
.Save( r
, B_LEGACYVERSION
);
359 // First of all the header
360 sal_uInt64 nStart
= SbiOpenRecord( r
, FileOffset::Module
, 1 );
363 eCharSet
= GetSOStoreTextEncoding( eCharSet
);
366 r
.WriteInt32( B_LEGACYVERSION
);
370 r
.WriteInt32( B_CURVERSION
);
372 r
.WriteInt32( eCharSet
)
373 .WriteInt32( nDimBase
)
374 .WriteInt16( static_cast<sal_uInt16
>(nFlags
) )
380 if( !aName
.isEmpty() && SbiGood( r
) )
382 nPos
= SbiOpenRecord( r
, FileOffset::Name
, 1 );
383 r
.WriteUniOrByteString( aName
, eCharSet
);
384 SbiCloseRecord( r
, nPos
);
387 if( !aComment
.isEmpty() && SbiGood( r
) )
389 nPos
= SbiOpenRecord( r
, FileOffset::Comment
, 1 );
390 r
.WriteUniOrByteString( aComment
, eCharSet
);
391 SbiCloseRecord( r
, nPos
);
394 if( !aOUSource
.isEmpty() && SbiGood( r
) )
396 nPos
= SbiOpenRecord( r
, FileOffset::Source
, 1 );
397 r
.WriteUniOrByteString( aOUSource
, eCharSet
);
398 SbiCloseRecord( r
, nPos
);
401 if( pCode
&& SbiGood( r
) )
403 nPos
= SbiOpenRecord( r
, FileOffset::PCode
, 1 );
406 PCodeBuffConvertor
< sal_uInt32
, sal_uInt16
> aNewToLegacy( reinterpret_cast<sal_uInt8
*>(pCode
.get()), nCodeSize
);
407 aNewToLegacy
.convert();
408 pLegacyPCode
.reset(reinterpret_cast<char*>(aNewToLegacy
.GetBuffer()));
409 nLegacyCodeSize
= aNewToLegacy
.GetSize();
410 r
.WriteBytes(pLegacyPCode
.get(), nLegacyCodeSize
);
414 r
.WriteBytes(pCode
.get(), nCodeSize
);
416 SbiCloseRecord( r
, nPos
);
419 if( !mvStringOffsets
.empty() )
421 nPos
= SbiOpenRecord( r
, FileOffset::StringPool
, mvStringOffsets
.size() );
423 // sal_uInt32 Offset of the Strings in the Stringblock
424 for( size_t i
= 0; i
< mvStringOffsets
.size() && SbiGood( r
); i
++ )
426 r
.WriteUInt32( mvStringOffsets
[ i
] );
428 // Then the String-Block
429 std::unique_ptr
<char[]> pByteStrings(new char[ nStringSize
]);
430 for( size_t i
= 0; i
< mvStringOffsets
.size(); i
++ )
432 sal_uInt16 nOff
= static_cast<sal_uInt16
>(mvStringOffsets
[ i
]);
433 OString
aStr(OUStringToOString(OUString(pStrings
.get() + nOff
), eCharSet
));
434 memcpy( pByteStrings
.get() + nOff
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof( char ) );
436 r
.WriteUInt32( nStringSize
);
437 r
.WriteBytes(pByteStrings
.get(), nStringSize
);
439 pByteStrings
.reset();
440 SbiCloseRecord( r
, nPos
);
442 // User defined types
445 sal_uInt16 nTypes
= rTypes
->Count();
448 nPos
= SbiOpenRecord( r
, FileOffset::UserTypes
, nTypes
);
450 for (sal_uInt16 i
= 0; i
< nTypes
; i
++)
452 SbxObject
* pType
= static_cast< SbxObject
* > ( rTypes
->Get(i
) );
453 OUString aTypeName
= pType
->GetClassName();
455 r
.WriteUniOrByteString( aTypeName
, eCharSet
);
457 SbxArray
*pTypeMembers
= pType
->GetProperties();
458 sal_uInt16 nTypeMembers
= pTypeMembers
->Count();
460 r
.WriteInt16(nTypeMembers
);
462 for (sal_uInt16 j
= 0; j
< nTypeMembers
; j
++)
465 SbxProperty
* pTypeElem
= static_cast< SbxProperty
* > ( pTypeMembers
->Get(j
) );
467 const OUString
& aElemName
= pTypeElem
->GetName();
468 r
.WriteUniOrByteString( aElemName
, eCharSet
);
470 SbxDataType dataType
= pTypeElem
->GetType();
471 r
.WriteInt16(dataType
);
473 SbxFlagBits nElemFlags
= pTypeElem
->GetFlags();
474 r
.WriteUInt32(static_cast< sal_uInt32
> (nElemFlags
) );
476 SbxBase
* pElemObject
= pTypeElem
->GetObject();
480 r
.WriteInt16(1); // has elem Object
482 if( dataType
== SbxOBJECT
)
484 // nested user defined types
485 // declared before use, so it is ok to reference it by name on load
486 SbxObject
* pNestedType
= static_cast< SbxObject
* > ( pElemObject
);
487 r
.WriteUniOrByteString( pNestedType
->GetClassName(), eCharSet
);
492 SbxDimArray
* pArray
= static_cast< SbxDimArray
* > ( pElemObject
);
494 bool bFixedSize
= pArray
->hasFixedSize();
500 sal_Int32 nDims
= pArray
->GetDims();
503 for (sal_Int32 d
= 0; d
< nDims
; d
++)
507 pArray
->GetDim32(d
, lBound
, uBound
);
508 r
.WriteInt32(lBound
).WriteInt32(uBound
);
513 r
.WriteInt16(0); // no elem Object
517 SbiCloseRecord( r
, nPos
);
520 // Set overall length
521 SbiCloseRecord( r
, nStart
);
529 void SbiImage::MakeStrings( short nSize
)
534 pStrings
.reset( new sal_Unicode
[ nStringSize
]);
535 mvStringOffsets
.resize(nSize
);
537 memset( mvStringOffsets
.data(), 0, nSize
* sizeof( sal_uInt32
) );
539 memset( pStrings
.get(), 0, nStringSize
* sizeof( sal_Unicode
) );
542 // Add a string to StringPool. The String buffer is dynamically
543 // growing in 1K-Steps
544 void SbiImage::AddString( const OUString
& r
)
546 if( nStringIdx
>= short(mvStringOffsets
.size()) )
552 sal_Int32 len
= r
.getLength() + 1;
553 sal_uInt32 needed
= nStringOff
+ len
;
554 if( needed
> 0xFFFFFF00 )
556 bError
= true; // out of mem!
558 else if( needed
> nStringSize
)
560 sal_uInt32 nNewLen
= needed
+ 1024;
561 nNewLen
&= 0xFFFFFC00; // trim to 1K border
562 std::unique_ptr
<sal_Unicode
[]> p(new sal_Unicode
[nNewLen
]);
563 memcpy( p
.get(), pStrings
.get(), nStringSize
* sizeof( sal_Unicode
) );
564 pStrings
= std::move(p
);
565 nStringSize
= sal::static_int_cast
< sal_uInt16
>(nNewLen
);
569 mvStringOffsets
[ nStringIdx
++ ] = nStringOff
;
570 memcpy( pStrings
.get() + nStringOff
, r
.getStr(), len
* sizeof( sal_Unicode
) );
571 nStringOff
= nStringOff
+ len
;
572 // Last String? The update the size of the buffer
573 if( nStringIdx
>= short(mvStringOffsets
.size()) )
575 nStringSize
= nStringOff
;
582 // The block was fetched by the compiler from class SbBuffer and
583 // is already created with new. Additionally it contains all Integers
584 // in Big Endian format, so can be directly read/written.
585 void SbiImage::AddCode( std::unique_ptr
<char[]> p
, sal_uInt32 s
)
587 pCode
= std::move(p
);
592 void SbiImage::AddType(SbxObject
const * pObject
)
596 rTypes
= new SbxArray
;
598 SbxObject
*pCopyObject
= new SbxObject(*pObject
);
599 rTypes
->Insert (pCopyObject
,rTypes
->Count());
602 void SbiImage::AddEnum(SbxObject
* pObject
) // Register enum type
606 rEnums
= new SbxArray
;
608 rEnums
->Insert( pObject
, rEnums
->Count() );
611 // Note: IDs start with 1
612 OUString
SbiImage::GetString( short nId
) const
614 if( nId
&& nId
<= short(mvStringOffsets
.size()) )
616 sal_uInt32 nOff
= mvStringOffsets
[ nId
- 1 ];
617 sal_Unicode
* pStr
= pStrings
.get() + nOff
;
619 // #i42467: Special treatment for vbNullChar
622 sal_uInt32 nNextOff
= (nId
< short(mvStringOffsets
.size())) ? mvStringOffsets
[ nId
] : nStringOff
;
623 sal_uInt32 nLen
= nNextOff
- nOff
- 1;
626 // Force length 1 and make char 0 afterwards
627 OUString
aNullCharStr( u
'\0');
633 return OUString(pStr
);
639 const SbxObject
* SbiImage::FindType (const OUString
& aTypeName
) const
641 return rTypes
.is() ? static_cast<SbxObject
*>(rTypes
->Find(aTypeName
,SbxClassType::Object
)) : nullptr;
644 sal_uInt16
SbiImage::CalcLegacyOffset( sal_Int32 nOffset
)
646 return SbiCodeGen::calcLegacyOffSet( reinterpret_cast<sal_uInt8
*>(pCode
.get()), nOffset
) ;
649 sal_uInt32
SbiImage::CalcNewOffset( sal_Int16 nOffset
)
651 return SbiCodeGen::calcNewOffSet( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
.get()), nOffset
) ;
654 void SbiImage::ReleaseLegacyBuffer()
656 pLegacyPCode
.reset();
660 bool SbiImage::ExceedsLegacyLimits()
662 return ( nStringSize
> 0xFF00 ) || ( CalcLegacyOffset( nCodeSize
) > 0xFF00 );
665 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */