Bump version to 5.0-43
[LibreOffice.git] / basic / source / classes / image.cxx
blob6726da140c6af29ff7b03addf1ac41cfad487b5e
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 <basic/sbx.hxx>
24 #include "sb.hxx"
25 #include <string.h>
26 #include "image.hxx"
27 #include <codegen.hxx>
28 #include <boost/scoped_array.hpp>
30 SbiImage::SbiImage()
32 pStringOff = NULL;
33 pStrings = NULL;
34 pCode = NULL;
35 pLegacyPCode = NULL;
36 nFlags = SbiImageFlags::NONE;
37 nStrings = 0;
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 delete[] pStringOff;
58 delete[] pStrings;
59 delete[] pCode;
60 ReleaseLegacyBuffer();
61 pStringOff = NULL;
62 pStrings = NULL;
63 pCode = NULL;
64 nFlags = SbiImageFlags::NONE;
65 nStrings = 0;
66 nStringSize= 0;
67 nLegacyCodeSize = 0;
68 nCodeSize = 0;
69 eCharSet = osl_getThreadTextEncoding();
70 nDimBase = 0;
71 bError = false;
74 /**************************************************************************
76 * Service-Routines for Load/Store
78 **************************************************************************/
80 bool SbiGood( SvStream& r )
82 return !r.IsEof() && r.GetError() == SVSTREAM_OK;
85 // Open Record
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 );
90 return nPos;
93 // Close Record
94 void SbiCloseRecord( SvStream& r, sal_Size nOff )
96 sal_Size nPos = r.Tell();
97 r.Seek( nOff + 2 );
98 r.WriteInt32(nPos - nOff - 8 );
99 r.Seek( nPos );
102 /**************************************************************************
104 * Load/Store
106 **************************************************************************/
108 bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion )
111 sal_uInt16 nSign, nCount;
112 sal_uInt32 nLen, nOff;
114 Clear();
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
119 sal_uInt32 lDimBase;
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);
130 eCharSet = nCharSet;
131 eCharSet = GetSOLoadTextEncoding( eCharSet );
132 bBadVer = ( nVersion > B_CURVERSION );
133 nDimBase = (sal_uInt16) lDimBase;
136 bool bLegacy = ( nVersion < B_EXT_IMG_VERSION );
138 sal_Size nNext;
139 while( ( nNext = r.Tell() ) < nLast )
142 r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount );
143 nNext += nLen + 8;
144 if( r.GetError() == SVSTREAM_OK )
146 switch( nSign )
148 case B_NAME:
149 aName = r.ReadUniOrByteString(eCharSet);
150 break;
151 case B_COMMENT:
152 aComment = r.ReadUniOrByteString(eCharSet );
153 break;
154 case B_SOURCE:
156 aOUSource = r.ReadUniOrByteString(eCharSet);
157 break;
159 case B_EXTSOURCE:
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);
174 break;
176 case B_PCODE:
177 if( bBadVer ) break;
178 pCode = new char[ nLen ];
179 nCodeSize = nLen;
180 r.Read( pCode, nCodeSize );
181 if ( bLegacy )
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.
200 break;
201 case B_PUBLICS:
202 case B_POOLDIR:
203 case B_SYMPOOL:
204 case B_LINERANGES:
205 break;
206 case B_STRINGPOOL:
208 if( bBadVer ) break;
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 );
219 short i;
220 for( i = 0; i < nStrings && SbiGood( r ); i++ )
222 r.ReadUInt32( nOff );
223 pStringOff[ i ] = (sal_uInt16) nOff;
225 r.ReadUInt32( nLen );
226 if( SbiGood( r ) )
228 delete [] pStrings;
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 ) );
241 break;
243 case B_USERTYPES:
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 );
277 sal_uInt32 aIntFlag;
278 r.ReadUInt32(aIntFlag);
279 SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag );
281 pTypeElem->SetFlags(nElemFlags);
283 sal_Int16 hasObject;
284 r.ReadInt16(hasObject);
286 if (hasObject == 1)
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 ) );
294 if (pNestedTypeObj)
296 SbxObject* pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj );
297 pTypeElem->PutObject( pCloneObj );
300 else
302 // an array
303 SbxDimArray* pArray = new SbxDimArray();
305 sal_Int16 isFixedSize;
306 r.ReadInt16(isFixedSize);
307 if (isFixedSize == 1)
308 pArray->setHasFixedSize( true );
310 sal_Int32 nDims;
311 r.ReadInt32(nDims);
312 for (sal_Int32 d = 0; d < nDims; d++)
314 sal_Int32 lBound;
315 sal_Int32 uBound;
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 );
331 AddType(pType);
334 break;
336 case B_MODEND:
337 goto done;
338 default:
339 break;
342 else
344 break;
346 r.Seek( nNext );
348 done:
349 r.Seek( nLast );
350 if( !SbiGood( r ) )
352 bError = true;
354 return !bError;
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() )
365 SbiImage aEmptyImg;
366 aEmptyImg.aName = aName;
367 aEmptyImg.Save( r, B_LEGACYVERSION );
368 return true;
370 // First of all the header
371 sal_uIntPtr nStart = SbiOpenRecord( r, B_MODULE, 1 );
372 sal_uIntPtr nPos;
374 eCharSet = GetSOStoreTextEncoding( eCharSet );
375 if ( bLegacy )
377 r.WriteInt32( B_LEGACYVERSION );
379 else
381 r.WriteInt32( B_CURVERSION );
383 r .WriteInt32( eCharSet )
384 .WriteInt32( nDimBase )
385 .WriteInt16( static_cast<sal_uInt16>(nFlags) )
386 .WriteInt16( 0 )
387 .WriteInt32( 0 )
388 .WriteInt32( 0 );
390 // Name?
391 if( !aName.isEmpty() && SbiGood( r ) )
393 nPos = SbiOpenRecord( r, B_NAME, 1 );
394 r.WriteUniOrByteString( aName, eCharSet );
395 SbiCloseRecord( r, nPos );
397 // Comment?
398 if( !aComment.isEmpty() && SbiGood( r ) )
400 nPos = SbiOpenRecord( r, B_COMMENT, 1 );
401 r.WriteUniOrByteString( aComment, eCharSet );
402 SbiCloseRecord( r, nPos );
404 // Source?
405 if( !aOUSource.isEmpty() && SbiGood( r ) )
407 nPos = SbiOpenRecord( r, B_SOURCE, 1 );
408 r.WriteUniOrByteString( aOUSource, eCharSet );
409 SbiCloseRecord( r, nPos );
411 // Binary data?
412 if( pCode && SbiGood( r ) )
414 nPos = SbiOpenRecord( r, B_PCODE, 1 );
415 if ( bLegacy )
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 );
424 else
426 r.Write( pCode, nCodeSize );
428 SbiCloseRecord( r, nPos );
430 // String-Pool?
431 if( nStrings )
433 nPos = SbiOpenRecord( r, B_STRINGPOOL, nStrings );
434 // For every String:
435 // sal_uInt32 Offset of the Strings in the Stringblock
436 short i;
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
457 if (rTypes)
459 sal_uInt16 nTypes = rTypes->Count();
460 if (nTypes > 0 )
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();
492 if (pElemObject)
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 );
503 else
505 // an array
506 SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject );
508 bool bFixedSize = pArray->hasFixedSize();
509 if (bFixedSize)
510 r.WriteInt16(1);
511 else
512 r.WriteInt16(0);
514 sal_Int32 nDims = pArray->GetDims();
515 r.WriteInt32(nDims);
517 for (sal_Int32 d = 0; d < nDims; d++)
519 sal_Int32 lBound;
520 sal_Int32 uBound;
521 pArray->GetDim32(d, lBound, uBound);
522 r.WriteInt32(lBound).WriteInt32(uBound);
526 else
527 r.WriteInt16(0); // no elem Object
531 SbiCloseRecord( r, nPos );
534 // Set overall length
535 SbiCloseRecord( r, nStart );
536 if( !SbiGood( r ) )
538 bError = true;
540 return !bError;
543 /**************************************************************************
545 * Routines called by the compiler
547 **************************************************************************/
549 void SbiImage::MakeStrings( short nSize )
551 nStrings = 0;
552 nStringIdx = 0;
553 nStringOff = 0;
554 nStringSize = 1024;
555 pStrings = new sal_Unicode[ nStringSize ];
556 pStringOff = new sal_uInt32[ nSize ];
557 nStrings = 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 )
568 bError = true;
570 if( !bError )
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 ) );
584 delete[] pStrings;
585 pStrings = p;
586 nStringSize = sal::static_int_cast< sal_uInt16 >(nNewLen);
588 if( !bError )
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;
602 // Add code block
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 )
608 pCode = p;
609 nCodeSize = s;
612 // Add user type
613 void SbiImage::AddType(SbxObject* pObject)
615 if( !rTypes.Is() )
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
625 if( !rEnums.Is() )
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
648 if( *pStr == 0 )
650 sal_uInt32 nNextOff = (nId < nStrings) ? pStringOff[ nId ] : nStringOff;
651 sal_uInt32 nLen = nNextOff - nOff - 1;
652 if( nLen == 1 )
654 // Force length 1 and make char 0 afterwards
655 OUString aNullCharStr( (sal_Unicode)0);
656 return aNullCharStr;
659 else
661 return OUString(pStr);
664 return OUString();
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;
685 pLegacyPCode = NULL;
686 nLegacyCodeSize = 0;
689 bool SbiImage::ExceedsLegacyLimits()
691 return ( nStringSize > 0xFF00L ) || ( CalcLegacyOffset( nCodeSize ) > 0xFF00L );
694 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */