Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / basic / source / classes / image.cxx
blob90cdaa7b7ed944ad90ea6837a1b63d8d933825c3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <sb.hxx>
26 #include <basic/sbxprop.hxx>
27 #include <string.h>
28 #include <image.hxx>
29 #include <codegen.hxx>
30 #include <memory>
32 SbiImage::SbiImage()
34 pStrings = nullptr;
35 pCode = nullptr;
36 pLegacyPCode = nullptr;
37 nFlags = SbiImageFlags::NONE;
38 nStringSize= 0;
39 nCodeSize = 0;
40 nLegacyCodeSize =
41 nDimBase = 0;
42 bInit =
43 bError = false;
44 bFirstInit = true;
45 eCharSet = osl_getThreadTextEncoding();
46 nStringIdx = 0;
47 nStringOff = 0;
50 SbiImage::~SbiImage()
52 Clear();
55 void SbiImage::Clear()
57 mvStringOffsets.clear();
58 pStrings.reset();
59 pCode.reset();
60 pLegacyPCode.reset();
61 nFlags = SbiImageFlags::NONE;
62 nStringSize= 0;
63 nLegacyCodeSize = 0;
64 nCodeSize = 0;
65 eCharSet = osl_getThreadTextEncoding();
66 nDimBase = 0;
67 bError = false;
70 static bool SbiGood( SvStream const & r )
72 return r.good();
75 // Open Record
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 );
81 return nPos;
84 // Close Record
85 static void SbiCloseRecord( SvStream& r, sal_uInt64 nOff )
87 sal_uInt64 nPos = r.Tell();
88 r.Seek( nOff + 2 );
89 r.WriteInt32(nPos - nOff - 8 );
90 r.Seek( nPos );
93 bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion )
96 sal_uInt16 nSign, nCount;
97 sal_uInt32 nLen, nOff;
99 Clear();
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
104 sal_uInt32 lDimBase;
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);
115 eCharSet = nCharSet;
116 eCharSet = GetSOLoadTextEncoding( eCharSet );
117 bBadVer = ( nVersion > B_CURVERSION );
118 nDimBase = static_cast<sal_uInt16>(lDimBase);
121 bool bLegacy = ( nVersion < B_EXT_IMG_VERSION );
123 sal_uInt64 nNext;
124 while( ( nNext = r.Tell() ) < nLast )
127 r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount );
128 nNext += nLen + 8;
129 if( r.GetError() == ERRCODE_NONE )
131 switch( static_cast<FileOffset>( nSign ) )
133 case FileOffset::Name:
134 aName = r.ReadUniOrByteString(eCharSet);
135 break;
136 case FileOffset::Comment:
137 aComment = r.ReadUniOrByteString(eCharSet );
138 break;
139 case FileOffset::Source:
141 aOUSource = r.ReadUniOrByteString(eCharSet);
142 break;
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);
159 break;
161 case FileOffset::PCode:
162 if( bBadVer ) break;
163 pCode.reset(new char[ nLen ]);
164 nCodeSize = nLen;
165 r.ReadBytes(pCode.get(), nCodeSize);
166 if ( bLegacy )
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.
184 break;
185 case FileOffset::Publics:
186 case FileOffset::PoolDir:
187 case FileOffset::SymPool:
188 case FileOffset::LineRanges:
189 break;
190 case FileOffset::StringPool:
192 if( bBadVer ) break;
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 );
209 if( SbiGood( r ) )
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 ) );
223 break;
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 );
267 sal_uInt32 aIntFlag;
268 r.ReadUInt32(aIntFlag);
269 SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag );
271 pTypeElem->SetFlags(nElemFlags);
273 sal_Int16 hasObject;
274 r.ReadInt16(hasObject);
276 if (hasObject == 1)
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 ) );
284 if (pNestedTypeObj)
286 SbxObject* pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj );
287 pTypeElem->PutObject( pCloneObj );
290 else
292 // an array
293 SbxDimArray* pArray = new SbxDimArray();
295 sal_Int16 isFixedSize;
296 r.ReadInt16(isFixedSize);
297 if (isFixedSize == 1)
298 pArray->setHasFixedSize( true );
300 sal_Int32 nDims;
301 r.ReadInt32(nDims);
302 for (sal_Int32 d = 0; d < nDims; d++)
304 sal_Int32 lBound;
305 sal_Int32 uBound;
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 );
321 AddType(pType);
323 break;
325 case FileOffset::ModEnd:
326 goto done;
327 default:
328 break;
331 else
333 break;
335 r.Seek( nNext );
337 done:
338 r.Seek( nLast );
339 if( !SbiGood( r ) )
341 bError = true;
343 return !bError;
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() )
354 SbiImage aEmptyImg;
355 aEmptyImg.aName = aName;
356 aEmptyImg.Save( r, B_LEGACYVERSION );
357 return true;
359 // First of all the header
360 sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 );
361 sal_uInt64 nPos;
363 eCharSet = GetSOStoreTextEncoding( eCharSet );
364 if ( bLegacy )
366 r.WriteInt32( B_LEGACYVERSION );
368 else
370 r.WriteInt32( B_CURVERSION );
372 r .WriteInt32( eCharSet )
373 .WriteInt32( nDimBase )
374 .WriteInt16( static_cast<sal_uInt16>(nFlags) )
375 .WriteInt16( 0 )
376 .WriteInt32( 0 )
377 .WriteInt32( 0 );
379 // Name?
380 if( !aName.isEmpty() && SbiGood( r ) )
382 nPos = SbiOpenRecord( r, FileOffset::Name, 1 );
383 r.WriteUniOrByteString( aName, eCharSet );
384 SbiCloseRecord( r, nPos );
386 // Comment?
387 if( !aComment.isEmpty() && SbiGood( r ) )
389 nPos = SbiOpenRecord( r, FileOffset::Comment, 1 );
390 r.WriteUniOrByteString( aComment, eCharSet );
391 SbiCloseRecord( r, nPos );
393 // Source?
394 if( !aOUSource.isEmpty() && SbiGood( r ) )
396 nPos = SbiOpenRecord( r, FileOffset::Source, 1 );
397 r.WriteUniOrByteString( aOUSource, eCharSet );
398 SbiCloseRecord( r, nPos );
400 // Binary data?
401 if( pCode && SbiGood( r ) )
403 nPos = SbiOpenRecord( r, FileOffset::PCode, 1 );
404 if ( bLegacy )
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);
412 else
414 r.WriteBytes(pCode.get(), nCodeSize);
416 SbiCloseRecord( r, nPos );
418 // String-Pool?
419 if( !mvStringOffsets.empty() )
421 nPos = SbiOpenRecord( r, FileOffset::StringPool, mvStringOffsets.size() );
422 // For every String:
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
443 if ( rTypes.is() )
445 sal_uInt16 nTypes = rTypes->Count();
446 if (nTypes > 0 )
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();
478 if (pElemObject)
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 );
489 else
491 // an array
492 SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject );
494 bool bFixedSize = pArray->hasFixedSize();
495 if (bFixedSize)
496 r.WriteInt16(1);
497 else
498 r.WriteInt16(0);
500 sal_Int32 nDims = pArray->GetDims();
501 r.WriteInt32(nDims);
503 for (sal_Int32 d = 0; d < nDims; d++)
505 sal_Int32 lBound;
506 sal_Int32 uBound;
507 pArray->GetDim32(d, lBound, uBound);
508 r.WriteInt32(lBound).WriteInt32(uBound);
512 else
513 r.WriteInt16(0); // no elem Object
517 SbiCloseRecord( r, nPos );
520 // Set overall length
521 SbiCloseRecord( r, nStart );
522 if( !SbiGood( r ) )
524 bError = true;
526 return !bError;
529 void SbiImage::MakeStrings( short nSize )
531 nStringIdx = 0;
532 nStringOff = 0;
533 nStringSize = 1024;
534 pStrings.reset( new sal_Unicode[ nStringSize ]);
535 mvStringOffsets.resize(nSize);
536 if (nSize != 0) {
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()) )
548 bError = true;
550 if( !bError )
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);
567 if( !bError )
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;
581 // Add code block
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);
588 nCodeSize = s;
591 // Add user type
592 void SbiImage::AddType(SbxObject const * pObject)
594 if( !rTypes.is() )
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
604 if( !rEnums.is() )
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
620 if( *pStr == 0 )
622 sal_uInt32 nNextOff = (nId < short(mvStringOffsets.size())) ? mvStringOffsets[ nId ] : nStringOff;
623 sal_uInt32 nLen = nNextOff - nOff - 1;
624 if( nLen == 1 )
626 // Force length 1 and make char 0 afterwards
627 OUString aNullCharStr( u'\0');
628 return aNullCharStr;
631 else
633 return OUString(pStr);
636 return OUString();
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();
657 nLegacyCodeSize = 0;
660 bool SbiImage::ExceedsLegacyLimits()
662 return ( nStringSize > 0xFF00 ) || ( CalcLegacyOffset( nCodeSize ) > 0xFF00 );
665 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */