/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>

#include <cassert>
#include <random>
#include <string_view>

#include <oox/ole/vbaexport.hxx>

#include <tools/stream.hxx>

#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
#include <com/sun/star/frame/XModel.hpp>

#include <ooo/vba/excel/XWorkbook.hpp>

#include <oox/helper/propertyset.hxx>
#include <oox/token/properties.hxx>

#include <sot/storage.hxx>

#include <comphelper/xmltools.hxx>
#include <utility>
#include <rtl/tencinfo.h>
#include <osl/thread.h>

#define VBA_EXPORT_DEBUG 0
#define VBA_USE_ORIGINAL_WM_STREAM 0
#define VBA_USE_ORIGINAL_DIR_STREAM 0
#define VBA_USE_ORIGINAL_PROJECT_STREAM 0
#define VBA_USE_ORIGINAL_VBA_PROJECT 0

/* Enable to see VBA Encryption work. For now the input data and length values
 * for encryption correspond to the case when the VBA macro is not protected.
 */
#define VBA_ENCRYPTION 1

namespace {

void exportString(SvStream& rStrm, std::u16string_view rString,
                  const rtl_TextEncoding eTextEncoding)
{
    OString aStringCorrectCodepage = OUStringToOString(rString, eTextEncoding);
    rStrm.WriteOString(aStringCorrectCodepage);
}

void exportUTF16String(SvStream& rStrm, const OUString& rString)
{
    sal_Int32 n = rString.getLength();
    const sal_Unicode* pString = rString.getStr();
    for (sal_Int32 i = 0; i < n; ++i)
    {
        sal_Unicode character = pString[i];
        rStrm.WriteUnicode(character);
    }
}

bool isWorkbook(const css::uno::Reference<css::uno::XInterface>& xInterface)
{
    css::uno::Reference<ooo::vba::excel::XWorkbook> xWorkbook(xInterface, css::uno::UNO_QUERY);
    return xWorkbook.is();
}

OUString createHexStringFromDigit(sal_uInt8 nDigit)
{
    OUString aString = OUString::number( nDigit, 16 );
    if(aString.getLength() == 1)
        aString = OUString::number(0) + aString;
    return aString.toAsciiUpperCase();
}

}

VBACompressionChunk::VBACompressionChunk(SvStream& rCompressedStream, const sal_uInt8* pData, std::size_t nChunkSize)
    : mrCompressedStream(rCompressedStream)
    , mpUncompressedData(pData)
    , mpCompressedChunkStream(nullptr)
    , mnChunkSize(nChunkSize)
    , mnCompressedCurrent(0)
    , mnCompressedEnd(0)
    , mnDecompressedCurrent(0)
    , mnDecompressedEnd(0)
{
}

static void setUInt16(sal_uInt8* pBuffer, size_t nPos, sal_uInt16 nVal)
{
    pBuffer[nPos] = nVal & 0xFF;
    pBuffer[nPos+1] = (nVal & 0xFF00) >> 8;
}

sal_uInt16 VBACompressionChunk::handleHeader(bool bCompressed)
{
    // handle header bytes
    size_t nSize = mnCompressedCurrent;
    sal_uInt16 nHeader = 0;
    PackCompressedChunkSize(nSize, nHeader);
    PackCompressedChunkFlag(bCompressed, nHeader);
    PackCompressedChunkSignature(nHeader);

    return nHeader;
}

// section 2.4.1.3.7
void VBACompressionChunk::write()
{

    mnDecompressedCurrent = 0;
    mnCompressedCurrent = 2;
    mnCompressedEnd = 4098;
    mnDecompressedEnd = std::min<sal_uInt64>(4096, mnChunkSize);

    // if that stream becomes larger than 4096 bytes then
    // we use the uncompressed stream
    sal_uInt8 pCompressedChunkStream[4098];
    mpCompressedChunkStream = pCompressedChunkStream;

    while (mnDecompressedCurrent < mnDecompressedEnd
            && mnCompressedCurrent < mnCompressedEnd)
    {
        // compress token sequence
        compressTokenSequence();
    }

    if (mnDecompressedCurrent < mnDecompressedEnd)
    {
        sal_uInt64 nChunkStart = mrCompressedStream.Tell();
        mrCompressedStream.WriteUInt16(0);
        writeRawChunk();
        mrCompressedStream.Seek(nChunkStart);
        sal_uInt16 nHeader = handleHeader(false);
        mrCompressedStream.WriteUInt16(nHeader);
    }
    else
    {
        sal_uInt16 nHeader = handleHeader(true);
        setUInt16(pCompressedChunkStream, 0, nHeader);
        // copy the compressed stream to our output stream
        mrCompressedStream.WriteBytes(pCompressedChunkStream, mnCompressedCurrent);
    }
}

// section 2.4.1.3.13
void VBACompressionChunk::PackCompressedChunkSize(size_t nSize, sal_uInt16& rHeader)
{
    sal_uInt16 nTemp1 = rHeader & 0xF000;
    sal_uInt16 nTemp2 = nSize - 3;
    rHeader = nTemp1 | nTemp2;
}

// section 2.4.1.3.16
void VBACompressionChunk::PackCompressedChunkFlag(bool bCompressed, sal_uInt16& rHeader)
{
    sal_uInt16 nTemp1 = rHeader & 0x7FFF;
    sal_uInt16 nTemp2 = static_cast<sal_uInt16>(bCompressed) << 15;
    rHeader = nTemp1 | nTemp2;
}

// section 2.4.1.3.14
void VBACompressionChunk::PackCompressedChunkSignature(sal_uInt16& rHeader)
{
    sal_Int32 nTemp = rHeader & 0x8FFFF;
    rHeader = nTemp | 0x3000;
}

// section 2.4.1.3.8
void VBACompressionChunk::compressTokenSequence()
{
    sal_uInt64 nFlagByteIndex = mnCompressedCurrent;
    sal_uInt8 nFlagByte = 0;
    ++mnCompressedCurrent;
    for (size_t index = 0; index <= 7; ++index)
    {
        if (mnDecompressedCurrent < mnDecompressedEnd
                && mnCompressedCurrent < mnCompressedEnd)
        {
            compressToken(index, nFlagByte);
        }
    }
    mpCompressedChunkStream[nFlagByteIndex] = nFlagByte;
}

// section 2.4.1.3.9
void VBACompressionChunk::compressToken(size_t index, sal_uInt8& nFlagByte)
{
    size_t nLength = 0;
    size_t nOffset = 0;
    match(nLength, nOffset);
    if (nOffset != 0)
    {
        if (mnCompressedCurrent + 1 < mnCompressedEnd)
        {
            sal_uInt16 nToken = CopyToken(nLength, nOffset);
            setUInt16(mpCompressedChunkStream, mnCompressedCurrent, nToken);
            SetFlagBit(index, true, nFlagByte);
            mnCompressedCurrent += 2;
            mnDecompressedCurrent += nLength;
        }
        else
        {
            mnCompressedCurrent = mnCompressedEnd;
        }
    }
    else
    {
        if (mnCompressedCurrent + 1 < mnCompressedEnd)
        {
            mpCompressedChunkStream[mnCompressedCurrent] = mpUncompressedData[mnDecompressedCurrent];
            ++mnCompressedCurrent;
            ++mnDecompressedCurrent;
        }
        else
        {
            mnCompressedCurrent = mnCompressedEnd;
        }
    }
}

// section 2.4.1.3.18
void VBACompressionChunk::SetFlagBit(size_t index, bool bVal, sal_uInt8& rFlag)
{
    size_t nTemp1 = static_cast<int>(bVal) << index;
    sal_uInt8 nTemp2 = rFlag & (~nTemp1);
    rFlag = nTemp2 | nTemp1;
}

// section 2.4.1.3.19.3
sal_uInt16 VBACompressionChunk::CopyToken(size_t nLength, size_t nOffset)
{
    sal_uInt16 nLengthMask = 0;
    sal_uInt16 nOffsetMask = 0;
    sal_uInt16 nBitCount = 0;
    sal_uInt16 nMaxLength;
    CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaxLength);
    sal_uInt16 nTemp1 = nOffset -1;
    sal_uInt16 nTemp2 = 16 - nBitCount;
    sal_uInt16 nTemp3 = nLength - 3;
    sal_uInt16 nToken = (nTemp1 << nTemp2) | nTemp3;
    return nToken;
}

// section 2.4.1.3.19.4
void VBACompressionChunk::match(size_t& rLength, size_t& rOffset)
{
    size_t nBestLen = 0;
    sal_Int32 nCandidate = mnDecompressedCurrent - 1;
    sal_Int32 nBestCandidate = nCandidate;
    while (nCandidate >= 0)
    {
        sal_Int32 nC = nCandidate;
        sal_Int32 nD = mnDecompressedCurrent;
        size_t nLen = 0;
        while (nD < static_cast<sal_Int32>(mnChunkSize) // TODO: check if this needs to be including a minus -1
                && mpUncompressedData[nC] == mpUncompressedData[nD])
        {
            ++nLen;
            ++nC;
            ++nD;
        }
        if (nLen > nBestLen)
        {
            nBestLen = nLen;
            nBestCandidate = nCandidate;
        }
        --nCandidate;
    }

    if (nBestLen >= 3)
    {
        sal_uInt16 nMaximumLength = 0;
        sal_uInt16 nLengthMask, nOffsetMask, nBitCount;
        CopyTokenHelp(nLengthMask, nOffsetMask, nBitCount, nMaximumLength);
        rLength = std::min<sal_uInt16>(nMaximumLength, nBestLen);
        rOffset = mnDecompressedCurrent - nBestCandidate;
    }
    else
    {
        rLength = 0;
        rOffset = 0;
    }
}

// section 2.4.1.3.19.1
void VBACompressionChunk::CopyTokenHelp(sal_uInt16& rLengthMask, sal_uInt16& rOffsetMask,
        sal_uInt16& rBitCount, sal_uInt16& rMaximumLength)
{
    sal_uInt16 nDifference = mnDecompressedCurrent;
    assert(nDifference <= 4096);
    assert(nDifference >= 1);
    if (nDifference >= 2049)
        rBitCount = 12;
    else if (nDifference >= 1025)
        rBitCount = 11;
    else if (nDifference >= 513)
        rBitCount = 10;
    else if (nDifference >= 257)
        rBitCount = 9;
    else if (nDifference >= 129)
        rBitCount = 8;
    else if (nDifference >= 65)
        rBitCount = 7;
    else if (nDifference >= 33)
        rBitCount = 6;
    else if (nDifference >= 17)
        rBitCount = 5;
    else
        rBitCount = 4;
    rLengthMask = 0xffff >> rBitCount;
    rOffsetMask = ~rLengthMask;
    rMaximumLength = rLengthMask + 3;
}

// section 2.4.1.3.10
void VBACompressionChunk::writeRawChunk()
{
    // we need to use up to 4096 bytes of the original stream
    // and fill the rest with padding
    mrCompressedStream.WriteBytes(mpUncompressedData, mnChunkSize);
    std::size_t nPadding = 4096 - mnChunkSize;
    for (size_t i = 0; i < nPadding; ++i)
    {
        mrCompressedStream.WriteUInt8(0);
    }
}

VBACompression::VBACompression(SvStream& rCompressedStream,
        SvMemoryStream& rUncompressedStream):
    mrCompressedStream(rCompressedStream),
    mrUncompressedStream(rUncompressedStream)
{
}

// section 2.4.1.3.6
void VBACompression::write()
{
    // section 2.4.1.1.1
    mrCompressedStream.WriteUInt8(0x01); // signature byte of a compressed container
    bool bStreamNotEnded = true;
    const sal_uInt8* pData = static_cast<const sal_uInt8*>(mrUncompressedStream.GetData());
    std::size_t nSize = mrUncompressedStream.GetEndOfData();
    std::size_t nRemainingSize = nSize;
    while(bStreamNotEnded)
    {
        std::size_t nChunkSize = std::min<size_t>(nRemainingSize, 4096);
        VBACompressionChunk aChunk(mrCompressedStream, &pData[nSize - nRemainingSize], nChunkSize);
        aChunk.write();

        // update the uncompressed chunk start marker
        nRemainingSize -= nChunkSize;
        bStreamNotEnded = nRemainingSize != 0;
    }
}

// section 2.4.3
#if VBA_ENCRYPTION

VBAEncryption::VBAEncryption(const sal_uInt8* pData, const sal_uInt16 length,
                             SvStream& rEncryptedData, sal_uInt8 nProjKey,
                             const rtl_TextEncoding eTextEncoding)
    :mpData(pData)
    ,mnLength(length)
    ,mrEncryptedData(rEncryptedData)
    ,mnUnencryptedByte1(0)
    ,mnEncryptedByte1(0)
    ,mnEncryptedByte2(0)
    ,mnProjKey(nProjKey)
    ,mnIgnoredLength(0)
    ,mnSeed(0x00)
    ,mnVersionEnc(0)
    ,meTextEncoding(eTextEncoding)
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 255);
    mnSeed = dis(gen);
}

void VBAEncryption::writeSeed()
{
    exportString(mrEncryptedData, createHexStringFromDigit(mnSeed), meTextEncoding);
}

void VBAEncryption::writeVersionEnc()
{
    static const sal_uInt8 mnVersion = 2; // the encrypted version
    mnVersionEnc = mnSeed ^ mnVersion;
    exportString(mrEncryptedData, createHexStringFromDigit(mnVersionEnc), meTextEncoding);
}

sal_uInt8 VBAEncryption::calculateProjKey(const OUString& rProjectKey)
{
    sal_uInt8 nProjKey = 0;
    sal_Int32 n = rProjectKey.getLength();
    const sal_Unicode* pString = rProjectKey.getStr();
    for (sal_Int32 i = 0; i < n; ++i)
    {
        sal_Unicode character = pString[i];
        nProjKey += character;
    }

    return nProjKey;
}

void VBAEncryption::writeProjKeyEnc()
{
    sal_uInt8 nProjKeyEnc = mnSeed ^ mnProjKey;
    exportString(mrEncryptedData, createHexStringFromDigit(nProjKeyEnc), meTextEncoding);
    mnUnencryptedByte1 = mnProjKey;
    mnEncryptedByte1 = nProjKeyEnc; // ProjKeyEnc
    mnEncryptedByte2 = mnVersionEnc; // VersionEnc
}

void VBAEncryption::writeIgnoredEnc()
{
    mnIgnoredLength = (mnSeed & 6) / 2;
    for(sal_Int32 i = 1; i <= mnIgnoredLength; ++i)
    {
        sal_uInt8 nTempValue = 0xBE; // Any value can be assigned here
        sal_uInt8 nByteEnc = nTempValue ^ (mnEncryptedByte2 + mnUnencryptedByte1);
        exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding);
        mnEncryptedByte2 = mnEncryptedByte1;
        mnEncryptedByte1 = nByteEnc;
        mnUnencryptedByte1 = nTempValue;
    }
}

void VBAEncryption::writeDataLengthEnc()
{
    sal_uInt16 temp = mnLength;
    for(sal_Int8 i = 0; i < 4; ++i)
    {
        sal_uInt8 nByte = temp & 0xFF;
        sal_uInt8 nByteEnc = nByte ^ (mnEncryptedByte2 + mnUnencryptedByte1);
        exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding);
        mnEncryptedByte2 = mnEncryptedByte1;
        mnEncryptedByte1 = nByteEnc;
        mnUnencryptedByte1 = nByte;
        temp >>= 8;
    }
}

void VBAEncryption::writeDataEnc()
{
    for(sal_Int16 i = 0; i < mnLength; i++)
    {
        sal_uInt8 nByteEnc = mpData[i] ^ (mnEncryptedByte2 + mnUnencryptedByte1);
        exportString(mrEncryptedData, createHexStringFromDigit(nByteEnc), meTextEncoding);
        mnEncryptedByte2 = mnEncryptedByte1;
        mnEncryptedByte1 = nByteEnc;
        mnUnencryptedByte1 = mpData[i];
    }
}

void VBAEncryption::write()
{
    writeSeed();
    writeVersionEnc();
    writeProjKeyEnc();
    writeIgnoredEnc();
    writeDataLengthEnc();
    writeDataEnc();
}

#endif

VbaExport::VbaExport(css::uno::Reference<css::frame::XModel> xModel):
    mxModel(std::move(xModel))
{
}

namespace {

// section 2.3.4.2.1.1
void writePROJECTSYSKIND(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0001); // id
    rStrm.WriteUInt32(0x00000004); // size
    rStrm.WriteUInt32(0x00000001); // SysKind, hard coded to 32-bin windows for now
}

// section 2.3.4.2.1.2
void writePROJECTLCID(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0002); // id
    rStrm.WriteUInt32(0x00000004); // size
    rStrm.WriteUInt32(0x00000409); // Lcid
}

// section 2.3.4.2.1.3
void writePROJECTLCIDINVOKE(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0014); // id
    rStrm.WriteUInt32(0x00000004); // size
    rStrm.WriteUInt32(0x00000409); // LcidInvoke
}

// section 2.3.4.2.1.4
void writePROJECTCODEPAGE(SvStream& rStrm, const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x0003); // id
    rStrm.WriteUInt32(0x00000002); // size
    rStrm.WriteUInt16(rtl_getWindowsCodePageFromTextEncoding(eTextEncoding)); // CodePage
}

//section 2.3.4.2.1.5
void writePROJECTNAME(SvStream& rStrm, std::u16string_view name, const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x0004); // id
    size_t sizeOfProjectName = name.size();
    rStrm.WriteUInt32(sizeOfProjectName); // sizeOfProjectName
    exportString(rStrm, name, eTextEncoding); // ProjectName
}

//section 2.3.4.2.1.6
void writePROJECTDOCSTRING(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0005); // id
    rStrm.WriteUInt32(0x00000000); // sizeOfDocString
    rStrm.WriteUInt16(0x0040); // Reserved
    rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode, MUST be even
}

//section 2.3.4.2.1.7
void writePROJECTHELPFILEPATH(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0006); // id
    rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile1
    rStrm.WriteUInt16(0x003D); // Reserved
    rStrm.WriteUInt32(0x00000000); // sizeOfHelpFile2
}

//section 2.3.4.2.1.8
void writePROJECTHELPCONTEXT(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0007); // id
    rStrm.WriteUInt32(0x00000004); // size
    rStrm.WriteUInt32(0x00000000); // HelpContext
}

//section 2.3.4.2.1.9
void writePROJECTLIBFLAGS(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0008); // id
    rStrm.WriteUInt32(0x00000004); // size
    rStrm.WriteUInt32(0x00000000); // ProjectLibFlags
}

//section 2.3.4.2.1.10
void writePROJECTVERSION(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0009); // id
    rStrm.WriteUInt32(0x00000004); // Reserved
    rStrm.WriteUInt32(1467127224); // VersionMajor // TODO: where is this magic number coming from
    rStrm.WriteUInt16(5); // VersionMinor // TODO: where is this magic number coming from
}

//section 2.3.4.2.1.11
void writePROJECTCONSTANTS(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x000C); // id
    rStrm.WriteUInt32(0x00000000); // sizeOfConstants
    rStrm.WriteUInt16(0x003C); // Reserved
    rStrm.WriteUInt32(0x00000000); // sizeOfConstantsUnicode
}

// section 2.3.4.2.1
void writePROJECTINFORMATION(SvStream& rStrm, std::u16string_view projectName,
                             const rtl_TextEncoding eTextEncoding)
{
    writePROJECTSYSKIND(rStrm);
    writePROJECTLCID(rStrm);
    writePROJECTLCIDINVOKE(rStrm);
    writePROJECTCODEPAGE(rStrm, eTextEncoding);
    writePROJECTNAME(rStrm, projectName, eTextEncoding);
    writePROJECTDOCSTRING(rStrm);
    writePROJECTHELPFILEPATH(rStrm);
    writePROJECTHELPCONTEXT(rStrm);
    writePROJECTLIBFLAGS(rStrm);
    writePROJECTVERSION(rStrm);
    writePROJECTCONSTANTS(rStrm);
}

// section 2.3.4.2.2.2
void writeREFERENCENAME(SvStream& rStrm, const OUString& name, const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x0016); // id
    sal_Int32 size = name.getLength();
    rStrm.WriteUInt32(size); // sizeOfName
    exportString(rStrm, name, eTextEncoding); // name
    rStrm.WriteUInt16(0x003E); // reserved
    sal_Int32 unicodesize = size * 2;
    rStrm.WriteUInt32(unicodesize); // sizeOfNameUnicode
    exportUTF16String(rStrm, name); // nameUnicode
}

// section 2.3.4.2.2.5
void writeREFERENCEREGISTERED(SvStream& rStrm, std::u16string_view libid,
                              const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x000D); // id
    size_t sizeOfLibid = libid.size();
    sal_Int32 size = sizeOfLibid + 10; // size of Libid, sizeOfLibid(4 bytes), reserved 1(4 bytes) and reserved 2(2 bytes)
    rStrm.WriteUInt32(size); // size
    rStrm.WriteUInt32(sizeOfLibid); // sizeOfLibid
    exportString(rStrm, libid, eTextEncoding); // Libid
    rStrm.WriteUInt32(0x00000000); // reserved 1
    rStrm.WriteUInt16(0x0000); // reserved 2
}

// section 2.3.4.2.2.1
void writeREFERENCE(SvStream& rStrm, const OUString& name, std::u16string_view libid,
                    const rtl_TextEncoding eTextEncoding)
{
    writeREFERENCENAME(rStrm, name, eTextEncoding);
    writeREFERENCEREGISTERED(rStrm, libid, eTextEncoding);
}

// section 2.3.4.2.2
void writePROJECTREFERENCES(SvStream& rStrm, const rtl_TextEncoding eTextEncoding)
{
    // TODO: find out where these references are coming from
    writeREFERENCE(rStrm, u"stdole"_ustr, u"*\\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\\Windows\\SysWOW64\\stdole2.tlb#OLE Automation", eTextEncoding);
    writeREFERENCE(rStrm, u"Office"_ustr, u"*\\G{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}#2.0#0#C:\\Program Files (x86)\\Common Files\\Microsoft Shared\\OFFICE14\\MSO.DLL#Microsoft Office 14.0 Object Library", eTextEncoding);
}

// section 2.3.4.2.3.1
void writePROJECTCOOKIE(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0013); // id
    rStrm.WriteUInt32(0x00000002); // size
    rStrm.WriteUInt16(0xFFFF); // cookie
}

// section 2.3.4.2.3.2.1
void writeMODULENAME(SvStream& rStrm, std::u16string_view name, const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x0019); // id
    sal_Int32 n = name.size(); // sizeOfModuleName
    rStrm.WriteUInt32(n);
    exportString(rStrm, name, eTextEncoding); // ModuleName
}

// section 2.3.4.2.3.2.2
void writeMODULENAMEUNICODE(SvStream& rStrm, const OUString& name)
{
    rStrm.WriteUInt16(0x0047); // id
    sal_Int32 n = name.getLength() * 2; // sizeOfModuleNameUnicode // TODO: better calculation for unicode string length
    rStrm.WriteUInt32(n);
    exportUTF16String(rStrm, name); // ModuleNameUnicode
}

// section 2.3.4.2.3.2.3
void writeMODULESTREAMNAME(SvStream& rStrm, const OUString& streamName,
                           const rtl_TextEncoding eTextEncoding)
{
    rStrm.WriteUInt16(0x001A); // id
    sal_Int32 n = streamName.getLength(); // sizeOfStreamName
    rStrm.WriteUInt32(n);
    exportString(rStrm, streamName, eTextEncoding); // StreamName
    rStrm.WriteUInt16(0x0032); // reserved
    rStrm.WriteUInt32(n * 2); // sizeOfStreamNameUnicode // TODO: better calculation for unicode string length
    exportUTF16String(rStrm, streamName); // StreamNameUnicode
}

// section 2.3.4.2.3.2.4
void writeMODULEDOCSTRING(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x001C); // id
    rStrm.WriteUInt32(0x00000000); // sizeOfDocString
    rStrm.WriteUInt16(0x0048); // reserved
    rStrm.WriteUInt32(0x00000000); // sizeOfDocStringUnicode
}

// section 2.3.4.2.3.2.5
void writeMODULEOFFSET(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x0031); // id
    rStrm.WriteUInt32(0x00000004); // sizeOfTextOffset
    rStrm.WriteUInt32(0x00000000); // TextOffset
}

// section 2.3.4.2.3.2.6
void writeMODULEHELPCONTEXT(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x001E); // id
    rStrm.WriteUInt32(0x00000004); // sizeOfHelpContext
    rStrm.WriteUInt32(0x00000000); // HelpContext
}

// section 2.3.4.2.3.2.7
void writeMODULECOOKIE(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x002C); // id
    rStrm.WriteUInt32(0x00000002); // sizeOfHelpContext
    rStrm.WriteUInt16(0xFFFF); // HelpContext
}

// section 2.3.4.2.3.2.8
void writeMODULETYPE(SvStream& rStrm, const sal_uInt16 type)
{
    if(type == 1)
        rStrm.WriteUInt16(0x0021); // id for a procedural module
    else
        rStrm.WriteUInt16(0x0022); // id for document, class or design module
    rStrm.WriteUInt32(0x00000000); // reserved
}

// section 2.3.4.2.3.2
void writePROJECTMODULE(SvStream& rStrm, const OUString& name, const sal_uInt16 type,
                        const rtl_TextEncoding eTextEncoding)
{
    writeMODULENAME(rStrm, name, eTextEncoding);
    writeMODULENAMEUNICODE(rStrm, name);
    writeMODULESTREAMNAME(rStrm, name, eTextEncoding);
    writeMODULEDOCSTRING(rStrm);
    writeMODULEOFFSET(rStrm);
    writeMODULEHELPCONTEXT(rStrm);
    writeMODULECOOKIE(rStrm);
    writeMODULETYPE(rStrm, type);
    rStrm.WriteUInt16(0x002B); // terminator
    rStrm.WriteUInt32(0x00000000); // reserved
}

// section 2.3.4.2.3
void writePROJECTMODULES(SvStream& rStrm,
                         const css::uno::Reference<css::container::XNameContainer>& xNameContainer,
                         const std::vector<sal_Int32>& rLibraryMap,
                         const rtl_TextEncoding eTextEncoding)
{
    const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
    sal_Int32 n = aElementNames.getLength();
    css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
    assert(xModuleInfo.is());

    // TODO: this whole part is document specific
    rStrm.WriteUInt16(0x000F); // id
    rStrm.WriteUInt32(0x00000002); // size of Count
    sal_Int16 count = n; // Number of modules // TODO: this is dependent on the document
    rStrm.WriteUInt16(count); // Count
    writePROJECTCOOKIE(rStrm);

    for (sal_Int32 i = 0; i < n; ++i)
    {
        const OUString& rModuleName = aElementNames[rLibraryMap[i]];
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
        writePROJECTMODULE(rStrm, rModuleName, aModuleInfo.ModuleType, eTextEncoding);
    }
}

// section 2.3.4.2
void exportDirStream(SvStream& rStrm,
                     const css::uno::Reference<css::container::XNameContainer>& xNameContainer,
                     const std::vector<sal_Int32>& rLibraryMap, const OUString& projectName,
                     const rtl_TextEncoding eTextEncoding)
{
    SvMemoryStream aDirStream(4096, 4096);

    writePROJECTINFORMATION(aDirStream, projectName, eTextEncoding);
    writePROJECTREFERENCES(aDirStream, eTextEncoding);
    writePROJECTMODULES(aDirStream, xNameContainer, rLibraryMap, eTextEncoding);
    aDirStream.WriteUInt16(0x0010); // terminator
    aDirStream.WriteUInt32(0x00000000); // reserved

#if VBA_EXPORT_DEBUG
    static constexpr OUStringLiteral aDirFileName(u"/tmp/vba_dir_out.bin");
    SvFileStream aDirStreamDebug(aDirFileName, StreamMode::READWRITE);
    aDirStream.Seek(0);
    aDirStreamDebug.WriteStream(aDirStream);
#endif

    VBACompression aCompression(rStrm, aDirStream);
    aCompression.write();
}

// section 2.3.4.3 Module Stream
void exportModuleStream(SvStream& rStrm, const OUString& rSourceCode, const OUString& aElementName,
                        css::script::ModuleInfo const& rInfo, const rtl_TextEncoding eTextEncoding)
{
    SvMemoryStream aModuleStream(4096, 4096);

    exportString(aModuleStream, Concat2View("Attribute VB_Name = \"" + aElementName + "\"\r\n"), eTextEncoding);
    if (rInfo.ModuleType == 4)
    {
        if (isWorkbook(rInfo.ModuleObject))
            exportString(aModuleStream, u"Attribute VB_Base = \"0{00020819-0000-0000-C000-000000000046}\"\r\n", eTextEncoding);
        else
            exportString(aModuleStream, u"Attribute VB_Base = \"0{00020820-0000-0000-C000-000000000046}\"\r\n", eTextEncoding);

        exportString(aModuleStream, u"Attribute VB_GlobalNameSpace = False\r\n", eTextEncoding);
        exportString(aModuleStream, u"Attribute VB_Creatable = False\r\n", eTextEncoding);
        exportString(aModuleStream, u"Attribute VB_PredeclaredId = True\r\n", eTextEncoding);
        exportString(aModuleStream, u"Attribute VB_Exposed = True\r\n", eTextEncoding);
        exportString(aModuleStream, u"Attribute VB_TemplateDerived = False\r\n", eTextEncoding);
        exportString(aModuleStream, u"Attribute VB_Customizable = True\r\n", eTextEncoding);
    }
    OUString aSourceCode = rSourceCode.replaceFirst("Option VBASupport 1\n", "");
    const sal_Int32 nPos = aSourceCode.indexOf("Rem Attribute VBA_ModuleType=");
    const sal_Int32 nEndPos = nPos != -1 ? aSourceCode.indexOf("\n", nPos) : -1;
    if (nPos != -1 && nEndPos != -1)
        aSourceCode = aSourceCode.replaceAt(nPos, nEndPos - nPos+1, u"");
    aSourceCode = aSourceCode.replaceAll("\n", "\r\n");
    exportString(aModuleStream, aSourceCode, eTextEncoding);

#if VBA_EXPORT_DEBUG
    OUString aModuleFileName("/tmp/vba_" + aElementName + "_out.bin");
    SvFileStream aModuleStreamDebug(aModuleFileName, StreamMode::READWRITE);
    aModuleStream.Seek(0);
    aModuleStreamDebug.WriteStream(aModuleStream);
#endif

    VBACompression aCompression(rStrm, aModuleStream);
    aCompression.write();
}

// section 2.3.4.1 _VBA_PROJECT Stream
void exportVBAProjectStream(SvStream& rStrm)
{
    rStrm.WriteUInt16(0x61CC); // Reserved1
    rStrm.WriteUInt16(0xFFFF); // Version
    rStrm.WriteUInt8(0x00); // Reserved2
    rStrm.WriteUInt16(0x0000); // Undefined
}

// section 2.3.1 PROJECT Stream
void exportPROJECTStream(SvStream& rStrm,
                         const css::uno::Reference<css::container::XNameContainer>& xNameContainer,
                         const OUString& projectName, const std::vector<sal_Int32>& rLibraryMap,
                         const rtl_TextEncoding eTextEncoding)
{
    const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
    sal_Int32 n = aElementNames.getLength();
    css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
    assert(xModuleInfo.is());

    // section 2.3.1.1ProjectProperties

    // section 2.3.1.2 ProjectId
    exportString(rStrm, u"ID=\"", eTextEncoding);
    OUString aProjectID
        = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
    exportString(rStrm, aProjectID, eTextEncoding);
    exportString(rStrm, u"\"\r\n", eTextEncoding);

    // section 2.3.1.3 ProjectModule
    for (sal_Int32 i = 0; i < n; ++i)
    {
        const OUString& rModuleName = aElementNames[rLibraryMap[i]];
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
        if(aModuleInfo.ModuleType == 1)
        {
            exportString(rStrm, Concat2View("Module=" + rModuleName + "\r\n"),
                         eTextEncoding);
        }
        else if(aModuleInfo.ModuleType == 4)
        {
            exportString(rStrm,
                         Concat2View("Document=" + rModuleName + "/&H00000000\r\n"),
                         eTextEncoding);
        }
    }

    // section 2.3.1.11 ProjectName
    exportString(rStrm, Concat2View("Name=\"" + projectName + "\"\r\n"), eTextEncoding);

    // section 2.3.1.12 ProjectHelpId
    exportString(rStrm, u"HelpContextID=\"0\"\r\n", eTextEncoding);

    // section 2.3.1.14 ProjectVersionCompat32
    exportString(rStrm, u"VersionCompatible32=\"393222000\"\r\n", eTextEncoding);

    // section 2.3.1.15 ProjectProtectionState
#if VBA_ENCRYPTION
    exportString(rStrm, u"CMG=\"", eTextEncoding);
    SvMemoryStream aProtectedStream(4096, 4096);
    aProtectedStream.WriteUInt32(0x00000000);
    const sal_uInt8* pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
    sal_uInt8 nProjKey = VBAEncryption::calculateProjKey(aProjectID);
    VBAEncryption aProtectionState(pData, 4, rStrm, nProjKey, eTextEncoding);
    aProtectionState.write();
    exportString(rStrm, u"\"\r\n", eTextEncoding);
#else
    exportString(rStrm, "CMG=\"BEBC9256EEAAA8AEA8AEA8AEA8AE\"\r\n", eTextEncoding);
#endif

    // section 2.3.1.16 ProjectPassword
#if VBA_ENCRYPTION
    exportString(rStrm, u"DPB=\"", eTextEncoding);
    aProtectedStream.Seek(0);
    aProtectedStream.WriteUInt8(0x00);
    pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
    VBAEncryption aProjectPassword(pData, 1, rStrm, nProjKey, eTextEncoding);
    aProjectPassword.write();
    exportString(rStrm, u"\"\r\n", eTextEncoding);
#else
    exportString(rStrm, "DPB=\"7C7E5014B0D3B1D3B1D3\"\r\n", eTextEncoding);
#endif

    // section 2.3.1.17 ProjectVisibilityState
#if VBA_ENCRYPTION
    exportString(rStrm, u"GC=\"", eTextEncoding);
    aProtectedStream.Seek(0);
    aProtectedStream.WriteUInt8(0xFF);
    pData = static_cast<const sal_uInt8*>(aProtectedStream.GetData());
    VBAEncryption aVisibilityState(pData, 1, rStrm, nProjKey, eTextEncoding);
    aVisibilityState.write();
    exportString(rStrm, u"\"\r\n\r\n", eTextEncoding);
#else
    exportString(rStrm, "GC=\"3A3816DAD5DBD5DB2A\"\r\n\r\n", eTextEncoding);
#endif

    // section 2.3.1.18 HostExtenders
    exportString(rStrm,
                 u"[Host Extender Info]\r\n"
                 "&H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000\r\n\r\n",
                 eTextEncoding);

    // section 2.3.1.19 ProjectWorkspace
    exportString(rStrm, u"[Workspace]\r\n", eTextEncoding);
    for (sal_Int32 i = 0; i < n; ++i)
    {
        const OUString& rModuleName = aElementNames[rLibraryMap[i]];
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
        if(aModuleInfo.ModuleType == 1)
        {
            exportString(rStrm, Concat2View(rModuleName + "=25, 25, 1439, 639, \r\n"),
                         eTextEncoding);
        }
        else
        {
            exportString(rStrm, Concat2View(rModuleName + "=0, 0, 0, 0, C\r\n"),
                         eTextEncoding);
        }
    }
}

// section 2.3.3.1 NAMEMAP
void writeNAMEMAP(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames,
        const std::vector<sal_Int32>& rLibraryMap, const rtl_TextEncoding eTextEncoding)
{
    int n = rElementNames.getLength();
    for(sal_Int32 i = 0; i < n; ++i)
    {
        const OUString& rModuleName = rElementNames[rLibraryMap[i]];
        exportString(rStrm, rModuleName, eTextEncoding);
        rStrm.WriteUInt8(0x00); // terminator
        exportUTF16String(rStrm, rModuleName);
        rStrm.WriteUInt16(0x0000); // terminator
    }
}

// section 2.3.3 PROJECTwm Stream
void exportPROJECTwmStream(SvStream& rStrm, const css::uno::Sequence<OUString>& rElementNames,
        const std::vector<sal_Int32>& rLibraryMap, const rtl_TextEncoding eTextEncoding)
{
    writeNAMEMAP(rStrm, rElementNames, rLibraryMap, eTextEncoding);
    rStrm.WriteUInt16(0x0000); // terminator
}

void getCorrectExportOrder(const css::uno::Reference<css::container::XNameContainer>& xNameContainer, std::vector<sal_Int32>& rLibraryMap)
{
    const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
    sal_Int32 n = aElementNames.getLength();
    css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);

    sal_Int32 nCurrentId = 0;
    // first all the non-document modules
    for (sal_Int32 i = 0; i < n; ++i)
    {
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
        if (aModuleInfo.ModuleType != 4)
        {
            rLibraryMap[nCurrentId] = i;
            ++nCurrentId;
        }
    }

    sal_Int32 nWorkbookIndex = -1;
    // then possibly the workbook module
    for (sal_Int32 i = 0; i < n; ++i)
    {
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
        bool bWorkbook = isWorkbook(aModuleInfo.ModuleObject);
        if (bWorkbook)
        {
            nWorkbookIndex = i;
            rLibraryMap[nCurrentId] = i;
            ++nCurrentId;
        }
    }

    // then the remaining modules
    for (sal_Int32 i = 0; i < n; ++i)
    {
        if (i == nWorkbookIndex)
            continue;

        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(aElementNames[i]);
        if (aModuleInfo.ModuleType == 4)
        {
            rLibraryMap[nCurrentId] = i;
            ++nCurrentId;
        }
    }
}

}

#if VBA_USE_ORIGINAL_WM_STREAM || VBA_USE_ORIGINAL_DIR_STREAM \
    || VBA_USE_ORIGINAL_PROJECT_STREAM || VBA_USE_ORIGINAL_VBA_PROJECT \
    || VBA_USE_ORIGINAL_DIR_STREAM
void addFileStreamToSotStream(const OUString& rPath, SotStorageStream& rStream)
{
    SvFileStream aFileStream(rPath, StreamMode::READWRITE);
    rStream.WriteStream(aFileStream);
}
#endif

void VbaExport::exportVBA(SotStorage* pRootStorage)
{
    css::uno::Reference<css::container::XNameContainer> xNameContainer = getBasicLibrary();
    if (!xNameContainer.is()) {
        return;
    }
    const css::uno::Sequence<OUString> aElementNames = xNameContainer->getElementNames();
    sal_Int32 n = aElementNames.getLength(); // get the number of modules
    // export the elements in the order MSO expects them
    // we store the index of the
    std::vector<sal_Int32> aLibraryMap(n, 0);
    getCorrectExportOrder(xNameContainer, aLibraryMap);

    // start here with the VBA export
    rtl::Reference<SotStorage> xVBAStream = pRootStorage->OpenSotStorage(u"VBA"_ustr, StreamMode::READWRITE);
    rtl::Reference<SotStorageStream> pDirStream = xVBAStream->OpenSotStream(u"dir"_ustr, StreamMode::READWRITE);

    rtl::Reference<SotStorageStream> pVBAProjectStream = xVBAStream->OpenSotStream(u"_VBA_PROJECT"_ustr, StreamMode::READWRITE);
    rtl::Reference<SotStorageStream> pPROJECTStream = pRootStorage->OpenSotStream(u"PROJECT"_ustr, StreamMode::READWRITE);
    rtl::Reference<SotStorageStream> pPROJECTwmStream = pRootStorage->OpenSotStream(u"PROJECTwm"_ustr, StreamMode::READWRITE);

    const rtl_TextEncoding eTextEncoding = getVBATextEncoding();

#if VBA_USE_ORIGINAL_WM_STREAM
    OUString aProjectwmPath = "/home/moggi/Documents/testfiles/vba/PROJECTwm";
    addFileStreamToSotStream(aProjectwmPath, *pPROJECTwmStream);
#else
    exportPROJECTwmStream(*pPROJECTwmStream, aElementNames, aLibraryMap, eTextEncoding);
#endif

#if VBA_USE_ORIGINAL_DIR_STREAM
    OUString aDirPath = "/home/moggi/Documents/testfiles/vba/VBA/dir";
    addFileStreamToSotStream(aDirPath, *pDirStream);
#else
    exportDirStream(*pDirStream, xNameContainer, aLibraryMap, getProjectName(), eTextEncoding);
#endif

#if VBA_USE_ORIGINAL_PROJECT_STREAM
    OUString aProjectPath = "/home/moggi/Documents/testfiles/vba/PROJECT";
    addFileStreamToSotStream(aProjectPath, *pPROJECTStream);
#else
    exportPROJECTStream(*pPROJECTStream, xNameContainer, getProjectName(), aLibraryMap,
                        eTextEncoding);
#endif

#if VBA_USE_ORIGINAL_VBA_PROJECT
    OUString a_VBA_ProjectPath = "/home/moggi/Documents/testfiles/vba/VBA/_VBA_PROJECT";
    addFileStreamToSotStream(a_VBA_ProjectPath, *pVBAProjectStream);
#else
    exportVBAProjectStream(*pVBAProjectStream);
#endif

#if VBA_USE_ORIGINAL_DIR_STREAM
    OUString aModule1Path = "/home/moggi/Documents/testfiles/vba/VBA/Module1";
    OUString aSheet1Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet1";
    OUString aSheet2Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet2";
    OUString aSheet3Path = "/home/moggi/Documents/testfiles/vba/VBA/Sheet3";
    OUString aWorkbookPath = "/home/moggi/Documents/testfiles/vba/VBA/ThisWorkbook";
    tools::SvRef<SotStorageStream> pModule1Stream = xVBAStream->OpenSotStream("Module1", StreamMode::READWRITE);
    tools::SvRef<SotStorageStream> pSheet1Stream = xVBAStream->OpenSotStream("Sheet1", StreamMode::READWRITE);
    tools::SvRef<SotStorageStream> pSheet2Stream = xVBAStream->OpenSotStream("Sheet2", StreamMode::READWRITE);
    tools::SvRef<SotStorageStream> pSheet3Stream = xVBAStream->OpenSotStream("Sheet3", StreamMode::READWRITE);
    tools::SvRef<SotStorageStream> pWorkbookStream = xVBAStream->OpenSotStream("ThisWorkbook", StreamMode::READWRITE);
    addFileStreamToSotStream(aModule1Path, *pModule1Stream);
    addFileStreamToSotStream(aSheet1Path, *pSheet1Stream);
    addFileStreamToSotStream(aSheet2Path, *pSheet2Stream);
    addFileStreamToSotStream(aSheet3Path, *pSheet3Stream);
    addFileStreamToSotStream(aWorkbookPath, *pWorkbookStream);

    pModule1Stream->Commit();
    pSheet1Stream->Commit();
    pSheet2Stream->Commit();
    pSheet3Stream->Commit();
    pWorkbookStream->Commit();
#else

    css::uno::Reference<css::script::vba::XVBAModuleInfo> xModuleInfo(xNameContainer, css::uno::UNO_QUERY);
    for (sal_Int32 i = 0; i < n; ++i)
    {
        const OUString& rModuleName = aElementNames[aLibraryMap[i]];
        rtl::Reference<SotStorageStream> pModuleStream = xVBAStream->OpenSotStream(rModuleName, StreamMode::READWRITE);
        css::uno::Any aCode = xNameContainer->getByName(rModuleName);
        css::script::ModuleInfo aModuleInfo = xModuleInfo->getModuleInfo(rModuleName);
        OUString aSourceCode;
        aCode >>= aSourceCode;
        exportModuleStream(*pModuleStream, aSourceCode, rModuleName, aModuleInfo, eTextEncoding);
        pModuleStream->Commit();
    }

#endif

    pVBAProjectStream->Commit();

    pDirStream->Commit();
    xVBAStream->Commit();
    pPROJECTStream->Commit();
    pPROJECTwmStream->Commit();
    pRootStorage->Commit();
}

css::uno::Reference<css::script::XLibraryContainer> VbaExport::getLibraryContainer() const
{
    oox::PropertySet aDocProp(mxModel);
    css::uno::Reference<css::script::XLibraryContainer> xLibContainer(aDocProp.getAnyProperty(oox::PROP_BasicLibraries), css::uno::UNO_QUERY);

    return xLibContainer;
}

css::uno::Reference<css::container::XNameContainer> VbaExport::getBasicLibrary() const
{
    css::uno::Reference<css::container::XNameContainer> xLibrary;
    try
    {
        css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
        OUString aProjectName = getProjectName();
        xLibrary.set( xLibContainer->getByName(aProjectName), css::uno::UNO_QUERY_THROW );
    }
    catch(...)
    {
    }

    return xLibrary;
}

bool VbaExport::containsVBAProject()
{
    css::uno::Reference<css::script::XLibraryContainer> xLibContainer = getLibraryContainer();
    if (!xLibContainer.is())
        return false;

    css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility (xLibContainer, css::uno::UNO_QUERY);
    if (!xVbaCompatibility.is())
        return false;

    bool bVBACompatibility = xVbaCompatibility->getVBACompatibilityMode();

    return bVBACompatibility;
}

OUString VbaExport::getProjectName() const
{
    css::uno::Reference<css::script::vba::XVBACompatibility> xVbaCompatibility(getLibraryContainer(), css::uno::UNO_QUERY);
    if (xVbaCompatibility.is())
        return xVbaCompatibility->getProjectName();

    return OUString();
}

rtl_TextEncoding VbaExport::getVBATextEncoding() const
{
    rtl_TextEncoding aTextEncoding = osl_getThreadTextEncoding();
    css::uno::Reference<css::beans::XPropertySet> xProps(getLibraryContainer(),
                                                         css::uno::UNO_QUERY);
    if (xProps.is())
        try
        {
            xProps->getPropertyValue(u"VBATextEncoding"_ustr) >>= aTextEncoding;
        }
        catch (const css::uno::Exception&)
        {
        }

    return aTextEncoding;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
