1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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>
31 
SbiImage()32 SbiImage::SbiImage()
33 {
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;
48 }
49 
~SbiImage()50 SbiImage::~SbiImage()
51 {
52     Clear();
53 }
54 
Clear()55 void SbiImage::Clear()
56 {
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;
68 }
69 
SbiGood(SvStream const & r)70 static bool SbiGood( SvStream const & r )
71 {
72     return r.good();
73 }
74 
75 // Open Record
SbiOpenRecord(SvStream & r,FileOffset nSignature,sal_uInt16 nElem)76 static sal_uInt64 SbiOpenRecord( SvStream& r, FileOffset nSignature, sal_uInt16 nElem )
77 {
78     sal_uInt64 nPos = r.Tell();
79     r.WriteUInt16( static_cast<sal_uInt16>( nSignature ) )
80         .WriteInt32( 0 ).WriteUInt16( nElem );
81     return nPos;
82 }
83 
84 // Close Record
SbiCloseRecord(SvStream & r,sal_uInt64 nOff)85 static void SbiCloseRecord( SvStream& r, sal_uInt64 nOff )
86 {
87     sal_uInt64 nPos = r.Tell();
88     r.Seek( nOff + 2 );
89     r.WriteInt32(nPos - nOff - 8 );
90     r.Seek( nPos );
91 }
92 
Load(SvStream & r,sal_uInt32 & nVersion)93 bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion )
94 {
95 
96     sal_uInt16 nSign, nCount;
97     sal_uInt32 nLen, nOff;
98 
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 ) )
110     {
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);
119     }
120 
121     bool bLegacy = ( nVersion < B_EXT_IMG_VERSION );
122 
123     sal_uInt64 nNext;
124     while( ( nNext = r.Tell() ) < nLast )
125     {
126 
127         r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount );
128         nNext += nLen + 8;
129         if( r.GetError() == ERRCODE_NONE )
130         {
131             switch( static_cast<FileOffset>( nSign ) )
132             {
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:
140             {
141                 aOUSource = r.ReadUniOrByteString(eCharSet);
142                 break;
143             }
144             case FileOffset::ExtSource:
145             {
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)
150                 {
151                     SAL_WARN("basic", "Parsing error: " << nMaxStrings <<
152                              " max possible entries, but " << nCount << " claimed, truncating");
153                     nCount = nMaxStrings;
154                 }
155                 for( sal_uInt16 j = 0; j < nCount; ++j)
156                 {
157                     aOUSource += r.ReadUniOrByteString(eCharSet);
158                 }
159                 break;
160             }
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 )
167                 {
168                     nLegacyCodeSize = static_cast<sal_uInt16>(nCodeSize);
169                     pLegacyPCode = std::move(pCode);
170 
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.
183                 }
184                 break;
185             case FileOffset::Publics:
186             case FileOffset::PoolDir:
187             case FileOffset::SymPool:
188             case FileOffset::LineRanges:
189                 break;
190             case FileOffset::StringPool:
191             {
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)
197                 {
198                     SAL_WARN("basic", "Parsing error: " << nMaxStrings <<
199                              " max possible entries, but " << nCount << " claimed, truncating");
200                     nCount = nMaxStrings;
201                 }
202                 MakeStrings( nCount );
203                 for( size_t i = 0; i < mvStringOffsets.size() && SbiGood( r ); i++ )
204                 {
205                     r.ReadUInt32( nOff );
206                     mvStringOffsets[ i ] = static_cast<sal_uInt16>(nOff);
207                 }
208                 r.ReadUInt32( nLen );
209                 if( SbiGood( r ) )
210                 {
211                     pStrings.reset(new sal_Unicode[ nLen ]);
212                     nStringSize = static_cast<sal_uInt16>(nLen);
213 
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++ )
217                     {
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 ) );
221                     }
222                 }
223                 break;
224             }
225             case FileOffset::UserTypes:
226             {
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)
232                 {
233                     SAL_WARN("basic", "Parsing error: " << nMaxRecords <<
234                              " max possible entries, but " << nCount << " claimed, truncating");
235                     nCount = nMaxRecords;
236                 }
237 
238                 // User defined types
239                 for (sal_uInt16 i = 0; i < nCount; i++)
240                 {
241                     OUString aTypeName = r.ReadUniOrByteString(eCharSet);
242 
243                     sal_uInt16 nTypeMembers;
244                     r.ReadUInt16(nTypeMembers);
245 
246                     const sal_uInt64 nMaxTypeMembers = r.remainingSize() / 8;
247                     if (nTypeMembers > nMaxTypeMembers)
248                     {
249                         SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers <<
250                                  " max possible entries, but " << nTypeMembers << " claimed, truncating");
251                         nTypeMembers = nMaxTypeMembers;
252                     }
253 
254                     SbxObject *pType = new SbxObject(aTypeName);
255                     SbxArray *pTypeMembers = pType->GetProperties();
256 
257                     for (sal_uInt16 j = 0; j < nTypeMembers; j++)
258                     {
259                         OUString aMemberName = r.ReadUniOrByteString(eCharSet);
260 
261                         sal_Int16 aIntMemberType;
262                         r.ReadInt16(aIntMemberType);
263                         SbxDataType aMemberType = static_cast< SbxDataType > ( aIntMemberType );
264 
265                         SbxProperty *pTypeElem = new SbxProperty( aMemberName, aMemberType );
266 
267                         sal_uInt32 aIntFlag;
268                         r.ReadUInt32(aIntFlag);
269                         SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag );
270 
271                         pTypeElem->SetFlags(nElemFlags);
272 
273                         sal_Int16 hasObject;
274                         r.ReadInt16(hasObject);
275 
276                         if (hasObject == 1)
277                         {
278                             if(aMemberType == SbxOBJECT)
279                             {
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)
285                                 {
286                                     SbxObject* pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj );
287                                     pTypeElem->PutObject( pCloneObj );
288                                 }
289                             }
290                             else
291                             {
292                                 // an array
293                                 SbxDimArray* pArray = new SbxDimArray();
294 
295                                 sal_Int16 isFixedSize;
296                                 r.ReadInt16(isFixedSize);
297                                 if (isFixedSize == 1)
298                                     pArray->setHasFixedSize( true );
299 
300                                 sal_Int32 nDims;
301                                 r.ReadInt32(nDims);
302                                 for (sal_Int32 d = 0; d < nDims; d++)
303                                 {
304                                     sal_Int32 lBound;
305                                     sal_Int32 uBound;
306                                     r.ReadInt32(lBound).ReadInt32(uBound);
307                                     pArray->unoAddDim32(lBound, uBound);
308                                 }
309 
310                                 pTypeElem->PutObject( pArray );
311                             }
312                         }
313 
314                         pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() );
315 
316                     }
317 
318                     pType->Remove( "Name", SbxClassType::DontCare );
319                     pType->Remove( "Parent", SbxClassType::DontCare );
320 
321                     AddType(pType);
322                 }
323                 break;
324             }
325             case FileOffset::ModEnd:
326                 goto done;
327             default:
328                 break;
329             }
330         }
331         else
332         {
333             break;
334         }
335         r.Seek( nNext );
336     }
337 done:
338     r.Seek( nLast );
339     if( !SbiGood( r ) )
340     {
341         bError = true;
342     }
343     return !bError;
344 }
345 
Save(SvStream & r,sal_uInt32 nVer)346 bool SbiImage::Save( SvStream& r, sal_uInt32 nVer )
347 {
348     bool bLegacy = ( nVer < B_EXT_IMG_VERSION );
349 
350     // detect if old code exceeds legacy limits
351     // if so, then disallow save
352     if ( bLegacy && ExceedsLegacyLimits() )
353     {
354         SbiImage aEmptyImg;
355         aEmptyImg.aName = aName;
356         aEmptyImg.Save( r, B_LEGACYVERSION );
357         return true;
358     }
359     // First of all the header
360     sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 );
361     sal_uInt64 nPos;
362 
363     eCharSet = GetSOStoreTextEncoding( eCharSet );
364     if ( bLegacy )
365     {
366         r.WriteInt32( B_LEGACYVERSION );
367     }
368     else
369     {
370         r.WriteInt32( B_CURVERSION );
371     }
372     r .WriteInt32( eCharSet )
373       .WriteInt32( nDimBase )
374       .WriteInt16( static_cast<sal_uInt16>(nFlags) )
375       .WriteInt16( 0 )
376       .WriteInt32( 0 )
377       .WriteInt32( 0 );
378 
379     // Name?
380     if( !aName.isEmpty() && SbiGood( r ) )
381     {
382         nPos = SbiOpenRecord( r, FileOffset::Name, 1 );
383         r.WriteUniOrByteString( aName, eCharSet );
384         SbiCloseRecord( r, nPos );
385     }
386     // Comment?
387     if( !aComment.isEmpty() && SbiGood( r ) )
388     {
389         nPos = SbiOpenRecord( r, FileOffset::Comment, 1 );
390         r.WriteUniOrByteString( aComment, eCharSet );
391         SbiCloseRecord( r, nPos );
392     }
393     // Source?
394     if( !aOUSource.isEmpty() && SbiGood( r ) )
395     {
396         nPos = SbiOpenRecord( r, FileOffset::Source, 1 );
397         r.WriteUniOrByteString( aOUSource, eCharSet );
398         SbiCloseRecord( r, nPos );
399     }
400     // Binary data?
401     if( pCode && SbiGood( r ) )
402     {
403         nPos = SbiOpenRecord( r, FileOffset::PCode, 1 );
404         if ( bLegacy )
405         {
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);
411         }
412         else
413         {
414             r.WriteBytes(pCode.get(), nCodeSize);
415         }
416         SbiCloseRecord( r, nPos );
417     }
418     // String-Pool?
419     if( !mvStringOffsets.empty() )
420     {
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++ )
425         {
426             r.WriteUInt32( mvStringOffsets[ i ] );
427         }
428         // Then the String-Block
429         std::unique_ptr<char[]> pByteStrings(new char[ nStringSize ]);
430         for( size_t i = 0; i < mvStringOffsets.size(); i++ )
431         {
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 ) );
435         }
436         r.WriteUInt32( nStringSize );
437         r.WriteBytes(pByteStrings.get(), nStringSize);
438 
439         pByteStrings.reset();
440         SbiCloseRecord( r, nPos );
441     }
442     // User defined types
443     if ( rTypes.is() )
444     {
445         sal_uInt16 nTypes = rTypes->Count();
446         if (nTypes > 0 )
447         {
448             nPos = SbiOpenRecord( r, FileOffset::UserTypes, nTypes );
449 
450             for (sal_uInt16 i = 0; i < nTypes; i++)
451             {
452                 SbxObject* pType = static_cast< SbxObject* > ( rTypes->Get(i) );
453                 OUString aTypeName = pType->GetClassName();
454 
455                 r.WriteUniOrByteString( aTypeName, eCharSet );
456 
457                 SbxArray  *pTypeMembers = pType->GetProperties();
458                 sal_uInt16 nTypeMembers = pTypeMembers->Count();
459 
460                 r.WriteInt16(nTypeMembers);
461 
462                 for (sal_uInt16 j = 0; j < nTypeMembers; j++)
463                 {
464 
465                     SbxProperty* pTypeElem = static_cast< SbxProperty* > ( pTypeMembers->Get(j) );
466 
467                     const OUString& aElemName = pTypeElem->GetName();
468                     r.WriteUniOrByteString( aElemName, eCharSet );
469 
470                     SbxDataType dataType =   pTypeElem->GetType();
471                     r.WriteInt16(dataType);
472 
473                     SbxFlagBits nElemFlags = pTypeElem->GetFlags();
474                     r.WriteUInt32(static_cast< sal_uInt32 > (nElemFlags) );
475 
476                     SbxBase* pElemObject = pTypeElem->GetObject();
477 
478                     if (pElemObject)
479                     {
480                         r.WriteInt16(1); // has elem Object
481 
482                         if( dataType == SbxOBJECT )
483                         {
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 );
488                         }
489                         else
490                         {
491                             // an array
492                             SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject );
493 
494                             bool bFixedSize = pArray->hasFixedSize();
495                             if (bFixedSize)
496                                 r.WriteInt16(1);
497                             else
498                                 r.WriteInt16(0);
499 
500                             sal_Int32 nDims = pArray->GetDims();
501                             r.WriteInt32(nDims);
502 
503                             for (sal_Int32 d = 0; d < nDims; d++)
504                             {
505                                 sal_Int32 lBound;
506                                 sal_Int32 uBound;
507                                 pArray->GetDim32(d, lBound, uBound);
508                                 r.WriteInt32(lBound).WriteInt32(uBound);
509                             }
510                         }
511                     }
512                     else
513                         r.WriteInt16(0); // no elem Object
514 
515                 }
516             }
517             SbiCloseRecord( r, nPos );
518         }
519     }
520     // Set overall length
521     SbiCloseRecord( r, nStart );
522     if( !SbiGood( r ) )
523     {
524         bError = true;
525     }
526     return !bError;
527 }
528 
MakeStrings(short nSize)529 void SbiImage::MakeStrings( short nSize )
530 {
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 ) );
538     }
539     memset( pStrings.get(), 0, nStringSize * sizeof( sal_Unicode ) );
540 }
541 
542 // Add a string to StringPool. The String buffer is dynamically
543 // growing in 1K-Steps
AddString(const OUString & r)544 void SbiImage::AddString( const OUString& r )
545 {
546     if( nStringIdx >= short(mvStringOffsets.size()) )
547     {
548         bError = true;
549     }
550     if( !bError )
551     {
552         sal_Int32  len = r.getLength() + 1;
553         sal_uInt32 needed = nStringOff + len;
554         if( needed > 0xFFFFFF00 )
555         {
556             bError = true;  // out of mem!
557         }
558         else if( needed > nStringSize )
559         {
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);
566         }
567         if( !bError )
568         {
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()) )
574             {
575                 nStringSize = nStringOff;
576             }
577         }
578     }
579 }
580 
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.
AddCode(std::unique_ptr<char[]> p,sal_uInt32 s)585 void SbiImage::AddCode( std::unique_ptr<char[]> p, sal_uInt32 s )
586 {
587     pCode = std::move(p);
588     nCodeSize = s;
589 }
590 
591 // Add user type
AddType(SbxObject const * pObject)592 void SbiImage::AddType(SbxObject const * pObject)
593 {
594     if( !rTypes.is() )
595     {
596         rTypes = new SbxArray;
597     }
598     SbxObject *pCopyObject = new SbxObject(*pObject);
599     rTypes->Insert (pCopyObject,rTypes->Count());
600 }
601 
AddEnum(SbxObject * pObject)602 void SbiImage::AddEnum(SbxObject* pObject) // Register enum type
603 {
604     if( !rEnums.is() )
605     {
606         rEnums = new SbxArray;
607     }
608     rEnums->Insert( pObject, rEnums->Count() );
609 }
610 
611 // Note: IDs start with 1
GetString(short nId) const612 OUString SbiImage::GetString( short nId ) const
613 {
614     if( nId && nId <= short(mvStringOffsets.size()) )
615     {
616         sal_uInt32 nOff = mvStringOffsets[ nId - 1 ];
617         sal_Unicode* pStr = pStrings.get() + nOff;
618 
619         // #i42467: Special treatment for vbNullChar
620         if( *pStr == 0 )
621         {
622             sal_uInt32 nNextOff = (nId < short(mvStringOffsets.size())) ? mvStringOffsets[ nId ] : nStringOff;
623             sal_uInt32 nLen = nNextOff - nOff - 1;
624             if( nLen == 1 )
625             {
626                 // Force length 1 and make char 0 afterwards
627                 OUString aNullCharStr( u'\0');
628                 return aNullCharStr;
629             }
630         }
631         else
632         {
633             return OUString(pStr);
634         }
635     }
636     return OUString();
637 }
638 
FindType(const OUString & aTypeName) const639 const SbxObject* SbiImage::FindType (const OUString& aTypeName) const
640 {
641     return rTypes.is() ? static_cast<SbxObject*>(rTypes->Find(aTypeName,SbxClassType::Object)) : nullptr;
642 }
643 
CalcLegacyOffset(sal_Int32 nOffset)644 sal_uInt16 SbiImage::CalcLegacyOffset( sal_Int32 nOffset )
645 {
646     return SbiCodeGen::calcLegacyOffSet( reinterpret_cast<sal_uInt8*>(pCode.get()), nOffset ) ;
647 }
648 
CalcNewOffset(sal_Int16 nOffset)649 sal_uInt32 SbiImage::CalcNewOffset( sal_Int16 nOffset )
650 {
651     return SbiCodeGen::calcNewOffSet( reinterpret_cast<sal_uInt8*>(pLegacyPCode.get()), nOffset ) ;
652 }
653 
ReleaseLegacyBuffer()654 void  SbiImage::ReleaseLegacyBuffer()
655 {
656     pLegacyPCode.reset();
657     nLegacyCodeSize = 0;
658 }
659 
ExceedsLegacyLimits()660 bool SbiImage::ExceedsLegacyLimits()
661 {
662     return ( nStringSize > 0xFF00 ) || ( CalcLegacyOffset( nCodeSize ) > 0xFF00 );
663 }
664 
665 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
666