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 <basic/sbx.hxx>
27 #include <codegen.hxx>
28 #include <boost/scoped_array.hpp>
36 nFlags
= SbiImageFlags::NONE
;
45 eCharSet
= osl_getThreadTextEncoding();
55 void SbiImage::Clear()
60 ReleaseLegacyBuffer();
64 nFlags
= SbiImageFlags::NONE
;
69 eCharSet
= osl_getThreadTextEncoding();
74 /**************************************************************************
76 * Service-Routines for Load/Store
78 **************************************************************************/
80 bool SbiGood( SvStream
& r
)
82 return !r
.IsEof() && r
.GetError() == SVSTREAM_OK
;
86 sal_uIntPtr
SbiOpenRecord( SvStream
& r
, sal_uInt16 nSignature
, sal_uInt16 nElem
)
88 sal_Size nPos
= r
.Tell();
89 r
.WriteUInt16( nSignature
).WriteInt32( 0 ).WriteUInt16( nElem
);
94 void SbiCloseRecord( SvStream
& r
, sal_Size nOff
)
96 sal_Size nPos
= r
.Tell();
98 r
.WriteInt32(nPos
- nOff
- 8 );
102 /**************************************************************************
106 **************************************************************************/
108 bool SbiImage::Load( SvStream
& r
, sal_uInt32
& nVersion
)
111 sal_uInt16 nSign
, nCount
;
112 sal_uInt32 nLen
, nOff
;
115 // Read Master-Record
116 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
117 sal_Size nLast
= r
.Tell() + nLen
;
118 sal_uInt32 nCharSet
; // System charset
120 sal_uInt16 nReserved1
;
121 sal_uInt32 nReserved2
;
122 sal_uInt32 nReserved3
;
123 bool bBadVer
= false;
124 if( nSign
== B_MODULE
)
126 sal_uInt16 nTmpFlags
;
127 r
.ReadUInt32( nVersion
).ReadUInt32( nCharSet
).ReadUInt32( lDimBase
)
128 .ReadUInt16( nTmpFlags
).ReadUInt16( nReserved1
).ReadUInt32( nReserved2
).ReadUInt32( nReserved3
);
129 nFlags
= static_cast<SbiImageFlags
>(nTmpFlags
);
131 eCharSet
= GetSOLoadTextEncoding( eCharSet
);
132 bBadVer
= ( nVersion
> B_CURVERSION
);
133 nDimBase
= (sal_uInt16
) lDimBase
;
136 bool bLegacy
= ( nVersion
< B_EXT_IMG_VERSION
);
139 while( ( nNext
= r
.Tell() ) < nLast
)
142 r
.ReadUInt16( nSign
).ReadUInt32( nLen
).ReadUInt16( nCount
);
144 if( r
.GetError() == SVSTREAM_OK
)
149 aName
= r
.ReadUniOrByteString(eCharSet
);
152 aComment
= r
.ReadUniOrByteString(eCharSet
);
156 aOUSource
= r
.ReadUniOrByteString(eCharSet
);
161 //assuming an empty string with just the lead 32bit/16bit len indicator
162 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
163 const size_t nMaxStrings
= r
.remainingSize() / nMinStringSize
;
164 if (nCount
> nMaxStrings
)
166 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
167 " max possible entries, but " << nCount
<< " claimed, truncating");
168 nCount
= nMaxStrings
;
170 for( sal_uInt16 j
= 0; j
< nCount
; ++j
)
172 aOUSource
+= r
.ReadUniOrByteString(eCharSet
);
178 pCode
= new char[ nLen
];
180 r
.Read( pCode
, nCodeSize
);
183 ReleaseLegacyBuffer(); // release any previously held buffer
184 nLegacyCodeSize
= (sal_uInt16
) nCodeSize
;
185 pLegacyPCode
= pCode
;
187 PCodeBuffConvertor
< sal_uInt16
, sal_uInt32
> aLegacyToNew( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
), nLegacyCodeSize
);
188 aLegacyToNew
.convert();
189 pCode
= reinterpret_cast<char*>(aLegacyToNew
.GetBuffer());
190 nCodeSize
= aLegacyToNew
.GetSize();
191 // we don't release the legacy buffer
192 // right now, thats because the module
193 // needs it to fix up the method
194 // nStart members. When that is done
195 // the module can release the buffer
196 // or it can wait until this routine
197 // is called again or when this class // destructs all of which will trigger
198 // release of the buffer.
209 //assuming an empty string with just the lead 32bit len indicator
210 const size_t nMinStringSize
= 4;
211 const size_t nMaxStrings
= r
.remainingSize() / nMinStringSize
;
212 if (nCount
> nMaxStrings
)
214 SAL_WARN("basic", "Parsing error: " << nMaxStrings
<<
215 " max possible entries, but " << nCount
<< " claimed, truncating");
216 nCount
= nMaxStrings
;
218 MakeStrings( nCount
);
220 for( i
= 0; i
< nStrings
&& SbiGood( r
); i
++ )
222 r
.ReadUInt32( nOff
);
223 pStringOff
[ i
] = (sal_uInt16
) nOff
;
225 r
.ReadUInt32( nLen
);
229 pStrings
= new sal_Unicode
[ nLen
];
230 nStringSize
= (sal_uInt16
) nLen
;
232 boost::scoped_array
<char> pByteStrings(new char[ nLen
]);
233 r
.Read( pByteStrings
.get(), nStringSize
);
234 for( short j
= 0; j
< nStrings
; j
++ )
236 sal_uInt16 nOff2
= (sal_uInt16
) pStringOff
[ j
];
237 OUString
aStr( pByteStrings
.get() + nOff2
, strlen(pByteStrings
.get() + nOff2
), eCharSet
);
238 memcpy( pStrings
+ nOff2
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof( sal_Unicode
) );
245 //assuming an empty string with just the lead 32bit/16bit len indicator
246 const size_t nMinStringSize
= (eCharSet
== RTL_TEXTENCODING_UNICODE
) ? 4 : 2;
247 const size_t nMinRecordSize
= nMinStringSize
+ sizeof(sal_Int16
);
248 const size_t nMaxRecords
= r
.remainingSize() / nMinRecordSize
;
249 if (nCount
> nMaxRecords
)
251 SAL_WARN("basic", "Parsing error: " << nMaxRecords
<<
252 " max possible entries, but " << nCount
<< " claimed, truncating");
253 nCount
= nMaxRecords
;
256 // User defined types
257 for (sal_uInt16 i
= 0; i
< nCount
; i
++)
259 OUString aTypeName
= r
.ReadUniOrByteString(eCharSet
);
261 sal_Int16 nTypeMembers
;
262 r
.ReadInt16(nTypeMembers
);
264 SbxObject
*pType
= new SbxObject(aTypeName
);
265 SbxArray
*pTypeMembers
= pType
->GetProperties();
267 for (sal_uInt16 j
= 0; j
< nTypeMembers
; j
++)
269 OUString aMemberName
= r
.ReadUniOrByteString(eCharSet
);
271 sal_Int16 aIntMemberType
;
272 r
.ReadInt16(aIntMemberType
);
273 SbxDataType aMemberType
= static_cast< SbxDataType
> ( aIntMemberType
);
275 SbxProperty
*pTypeElem
= new SbxProperty( aMemberName
, aMemberType
);
278 r
.ReadUInt32(aIntFlag
);
279 SbxFlagBits nElemFlags
= static_cast< SbxFlagBits
> ( aIntFlag
);
281 pTypeElem
->SetFlags(nElemFlags
);
284 r
.ReadInt16(hasObject
);
288 if(aMemberType
== SbxOBJECT
)
290 // nested user defined types
291 // declared before use, so it is ok to reference it by name on load
292 OUString aNestedTypeName
= r
.ReadUniOrByteString(eCharSet
);
293 SbxObject
* pNestedTypeObj
= static_cast< SbxObject
* >( rTypes
->Find( aNestedTypeName
, SbxCLASS_OBJECT
) );
296 SbxObject
* pCloneObj
= cloneTypeObjectImpl( *pNestedTypeObj
);
297 pTypeElem
->PutObject( pCloneObj
);
303 SbxDimArray
* pArray
= new SbxDimArray();
305 sal_Int16 isFixedSize
;
306 r
.ReadInt16(isFixedSize
);
307 if (isFixedSize
== 1)
308 pArray
->setHasFixedSize( true );
312 for (sal_Int32 d
= 0; d
< nDims
; d
++)
316 r
.ReadInt32(lBound
).ReadInt32(uBound
);
317 pArray
->unoAddDim32(lBound
, uBound
);
320 pTypeElem
->PutObject( pArray
);
324 pTypeMembers
->Insert( pTypeElem
, pTypeMembers
->Count() );
328 pType
->Remove( OUString("Name"), SbxCLASS_DONTCARE
);
329 pType
->Remove( OUString("Parent"), SbxCLASS_DONTCARE
);
357 bool SbiImage::Save( SvStream
& r
, sal_uInt32 nVer
)
359 bool bLegacy
= ( nVer
< B_EXT_IMG_VERSION
);
361 // detect if old code exceeds legacy limits
362 // if so, then disallow save
363 if ( bLegacy
&& ExceedsLegacyLimits() )
366 aEmptyImg
.aName
= aName
;
367 aEmptyImg
.Save( r
, B_LEGACYVERSION
);
370 // First of all the header
371 sal_uIntPtr nStart
= SbiOpenRecord( r
, B_MODULE
, 1 );
374 eCharSet
= GetSOStoreTextEncoding( eCharSet
);
377 r
.WriteInt32( B_LEGACYVERSION
);
381 r
.WriteInt32( B_CURVERSION
);
383 r
.WriteInt32( eCharSet
)
384 .WriteInt32( nDimBase
)
385 .WriteInt16( static_cast<sal_uInt16
>(nFlags
) )
391 if( !aName
.isEmpty() && SbiGood( r
) )
393 nPos
= SbiOpenRecord( r
, B_NAME
, 1 );
394 r
.WriteUniOrByteString( aName
, eCharSet
);
395 SbiCloseRecord( r
, nPos
);
398 if( !aComment
.isEmpty() && SbiGood( r
) )
400 nPos
= SbiOpenRecord( r
, B_COMMENT
, 1 );
401 r
.WriteUniOrByteString( aComment
, eCharSet
);
402 SbiCloseRecord( r
, nPos
);
405 if( !aOUSource
.isEmpty() && SbiGood( r
) )
407 nPos
= SbiOpenRecord( r
, B_SOURCE
, 1 );
408 r
.WriteUniOrByteString( aOUSource
, eCharSet
);
409 SbiCloseRecord( r
, nPos
);
412 if( pCode
&& SbiGood( r
) )
414 nPos
= SbiOpenRecord( r
, B_PCODE
, 1 );
417 ReleaseLegacyBuffer(); // release any previously held buffer
418 PCodeBuffConvertor
< sal_uInt32
, sal_uInt16
> aNewToLegacy( reinterpret_cast<sal_uInt8
*>(pCode
), nCodeSize
);
419 aNewToLegacy
.convert();
420 pLegacyPCode
= reinterpret_cast<char*>(aNewToLegacy
.GetBuffer());
421 nLegacyCodeSize
= aNewToLegacy
.GetSize();
422 r
.Write( pLegacyPCode
, nLegacyCodeSize
);
426 r
.Write( pCode
, nCodeSize
);
428 SbiCloseRecord( r
, nPos
);
433 nPos
= SbiOpenRecord( r
, B_STRINGPOOL
, nStrings
);
435 // sal_uInt32 Offset of the Strings in the Stringblock
438 for( i
= 0; i
< nStrings
&& SbiGood( r
); i
++ )
440 r
.WriteUInt32( pStringOff
[ i
] );
442 // Then the String-Block
443 boost::scoped_array
<char> pByteStrings(new char[ nStringSize
]);
444 for( i
= 0; i
< nStrings
; i
++ )
446 sal_uInt16 nOff
= (sal_uInt16
) pStringOff
[ i
];
447 OString
aStr(OUStringToOString(OUString(pStrings
+ nOff
), eCharSet
));
448 memcpy( pByteStrings
.get() + nOff
, aStr
.getStr(), (aStr
.getLength() + 1) * sizeof( char ) );
450 r
.WriteUInt32( nStringSize
);
451 r
.Write( pByteStrings
.get(), nStringSize
);
453 pByteStrings
.reset();
454 SbiCloseRecord( r
, nPos
);
456 // User defined types
459 sal_uInt16 nTypes
= rTypes
->Count();
462 nPos
= SbiOpenRecord( r
, B_USERTYPES
, nTypes
);
464 for (sal_uInt16 i
= 0; i
< nTypes
; i
++)
466 SbxObject
* pType
= static_cast< SbxObject
* > ( rTypes
->Get(i
) );
467 OUString aTypeName
= pType
->GetClassName();
469 r
.WriteUniOrByteString( aTypeName
, eCharSet
);
471 SbxArray
*pTypeMembers
= pType
->GetProperties();
472 sal_uInt16 nTypeMembers
= pTypeMembers
->Count();
474 r
.WriteInt16(nTypeMembers
);
476 for (sal_uInt16 j
= 0; j
< nTypeMembers
; j
++)
479 SbxProperty
* pTypeElem
= static_cast< SbxProperty
* > ( pTypeMembers
->Get(j
) );
481 OUString aElemName
= pTypeElem
->GetName();
482 r
.WriteUniOrByteString( aElemName
, eCharSet
);
484 SbxDataType dataType
= pTypeElem
->GetType();
485 r
.WriteInt16(dataType
);
487 SbxFlagBits nElemFlags
= pTypeElem
->GetFlags();
488 r
.WriteUInt32(static_cast< sal_uInt32
> (nElemFlags
) );
490 SbxBase
* pElemObject
= pTypeElem
->GetObject();
494 r
.WriteInt16(1); // has elem Object
496 if( dataType
== SbxOBJECT
)
498 // nested user defined types
499 // declared before use, so it is ok to reference it by name on load
500 SbxObject
* pNestedType
= static_cast< SbxObject
* > ( pElemObject
);
501 r
.WriteUniOrByteString( pNestedType
->GetClassName(), eCharSet
);
506 SbxDimArray
* pArray
= static_cast< SbxDimArray
* > ( pElemObject
);
508 bool bFixedSize
= pArray
->hasFixedSize();
514 sal_Int32 nDims
= pArray
->GetDims();
517 for (sal_Int32 d
= 0; d
< nDims
; d
++)
521 pArray
->GetDim32(d
, lBound
, uBound
);
522 r
.WriteInt32(lBound
).WriteInt32(uBound
);
527 r
.WriteInt16(0); // no elem Object
531 SbiCloseRecord( r
, nPos
);
534 // Set overall length
535 SbiCloseRecord( r
, nStart
);
543 /**************************************************************************
545 * Routines called by the compiler
547 **************************************************************************/
549 void SbiImage::MakeStrings( short nSize
)
555 pStrings
= new sal_Unicode
[ nStringSize
];
556 pStringOff
= new sal_uInt32
[ nSize
];
558 memset( pStringOff
, 0, nSize
* sizeof( sal_uInt32
) );
559 memset( pStrings
, 0, nStringSize
* sizeof( sal_Unicode
) );
562 // Add a string to StringPool. The String buffer is dynamically
563 // growing in 1K-Steps
564 void SbiImage::AddString( const OUString
& r
)
566 if( nStringIdx
>= nStrings
)
572 sal_Int32 len
= r
.getLength() + 1;
573 sal_uInt32 needed
= nStringOff
+ len
;
574 if( needed
> 0xFFFFFF00L
)
576 bError
= true; // out of mem!
578 else if( needed
> nStringSize
)
580 sal_uInt32 nNewLen
= needed
+ 1024;
581 nNewLen
&= 0xFFFFFC00; // trim to 1K border
582 sal_Unicode
* p
= new sal_Unicode
[nNewLen
];
583 memcpy( p
, pStrings
, nStringSize
* sizeof( sal_Unicode
) );
586 nStringSize
= sal::static_int_cast
< sal_uInt16
>(nNewLen
);
590 pStringOff
[ nStringIdx
++ ] = nStringOff
;
591 memcpy( pStrings
+ nStringOff
, r
.getStr(), len
* sizeof( sal_Unicode
) );
592 nStringOff
= nStringOff
+ len
;
593 // Last String? The update the size of the buffer
594 if( nStringIdx
>= nStrings
)
596 nStringSize
= nStringOff
;
603 // The block was fetched by the compiler from class SbBuffer and
604 // is already created with new. Additionally it contains all Integers
605 // in Big Endian format, so can be directly read/written.
606 void SbiImage::AddCode( char* p
, sal_uInt32 s
)
613 void SbiImage::AddType(SbxObject
* pObject
)
617 rTypes
= new SbxArray
;
619 SbxObject
*pCopyObject
= new SbxObject(*pObject
);
620 rTypes
->Insert (pCopyObject
,rTypes
->Count());
623 void SbiImage::AddEnum(SbxObject
* pObject
) // Register enum type
627 rEnums
= new SbxArray
;
629 rEnums
->Insert( pObject
, rEnums
->Count() );
633 /**************************************************************************
635 * Accessing the image
637 **************************************************************************/
639 // Note: IDs start with 1
640 OUString
SbiImage::GetString( short nId
) const
642 if( nId
&& nId
<= nStrings
)
644 sal_uInt32 nOff
= pStringOff
[ nId
- 1 ];
645 sal_Unicode
* pStr
= pStrings
+ nOff
;
647 // #i42467: Special treatment for vbNullChar
650 sal_uInt32 nNextOff
= (nId
< nStrings
) ? pStringOff
[ nId
] : nStringOff
;
651 sal_uInt32 nLen
= nNextOff
- nOff
- 1;
654 // Force length 1 and make char 0 afterwards
655 OUString
aNullCharStr( (sal_Unicode
)0);
661 return OUString(pStr
);
667 const SbxObject
* SbiImage::FindType (const OUString
& aTypeName
) const
669 return rTypes
.Is() ? static_cast<SbxObject
*>(rTypes
->Find(aTypeName
,SbxCLASS_OBJECT
)) : NULL
;
672 sal_uInt16
SbiImage::CalcLegacyOffset( sal_Int32 nOffset
)
674 return SbiCodeGen::calcLegacyOffSet( reinterpret_cast<sal_uInt8
*>(pCode
), nOffset
) ;
677 sal_uInt32
SbiImage::CalcNewOffset( sal_Int16 nOffset
)
679 return SbiCodeGen::calcNewOffSet( reinterpret_cast<sal_uInt8
*>(pLegacyPCode
), nOffset
) ;
682 void SbiImage::ReleaseLegacyBuffer()
684 delete[] pLegacyPCode
;
689 bool SbiImage::ExceedsLegacyLimits()
691 return ( nStringSize
> 0xFF00L
) || ( CalcLegacyOffset( nCodeSize
) > 0xFF00L
);
694 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */