244 lines
6.1 KiB
C++
244 lines
6.1 KiB
C++
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2023
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
|
|
#include "VbaBinary.h"
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
|
|
|
|
|
|
CVbaFile::~CVbaFile()
|
|
{
|
|
streams.clear();
|
|
|
|
if (storage_)delete storage_;
|
|
storage_ = NULL;
|
|
}
|
|
|
|
bool CVbaFile::isError()
|
|
{
|
|
if (storage_ != NULL) return false;
|
|
return true;
|
|
}
|
|
|
|
bool CVbaFile::Open(const std::wstring & file_path)
|
|
{
|
|
if (storage_) delete storage_;
|
|
storage_ = NULL;
|
|
|
|
storage_ = new POLE::Storage(file_path.c_str());
|
|
if (storage_ == NULL) return false;
|
|
|
|
if (storage_->open(false, false) == false)
|
|
{
|
|
delete storage_;
|
|
storage_ = NULL;
|
|
}
|
|
|
|
if (storage_ == NULL) return false;
|
|
return true;
|
|
}
|
|
CVbaFile::CVbaFile(const std::wstring & file_path)
|
|
{
|
|
storage_ = NULL;
|
|
Open(file_path);
|
|
}
|
|
CVbaFileStreamPtr CVbaFile::getNamedStream(const std::wstring& name, _UINT32 offset)
|
|
{
|
|
if (!streams[name])
|
|
{
|
|
POLE::Stream * pStream = openStream(name.c_str());
|
|
if (pStream)
|
|
streams[name].reset(new CVbaFileStream(pStream, offset));
|
|
}
|
|
return streams[name];
|
|
}
|
|
|
|
void CVbaFile::closeNamedStream(const std::wstring& name)
|
|
{
|
|
streams[name].reset();
|
|
}
|
|
|
|
POLE::Stream* CVbaFile::openStream(const std::wstring & stream_name)
|
|
{
|
|
if (storage_ == NULL) return NULL;
|
|
|
|
POLE::Stream* pStream = new POLE::Stream(storage_, stream_name);
|
|
if (pStream == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if ((pStream) && (pStream->size() > 0))
|
|
return pStream;
|
|
else return NULL;
|
|
}
|
|
const unsigned char VBASTREAM_SIGNATURE = 1;
|
|
|
|
const _UINT16 CHUNK_SIGMASK = 0x7000;
|
|
const _UINT16 CHUNK_SIG = 0x3000;
|
|
const _UINT16 CHUNK_COMPRESSED = 0x8000;
|
|
const _UINT16 CHUNK_LENMASK = 0x0FFF;
|
|
|
|
inline _UINT16 extract(_UINT16 nBitField, unsigned char nStartBit, unsigned char nBitCount)
|
|
{
|
|
_UINT64 nMask = 1; nMask <<= nBitCount; --nMask;
|
|
return static_cast< _UINT16 >(nMask & (nBitField >> nStartBit));
|
|
}
|
|
CVbaFileStream::CVbaFileStream(POLE::Stream* stream, _UINT32 offset)
|
|
{
|
|
size_t dataSize = stream->size();
|
|
|
|
unsigned char sig = 0;
|
|
stream->read(&sig, 1);
|
|
|
|
if (sig != 1)
|
|
{
|
|
stream->seek(0);
|
|
arrChunks.resize(dataSize);
|
|
stream->read(arrChunks.data(), dataSize);
|
|
|
|
return;
|
|
}
|
|
dataSize -= 1;
|
|
|
|
if (offset > dataSize)
|
|
{
|
|
return; //error;
|
|
}
|
|
|
|
stream->seek(stream->tell() + offset);
|
|
dataSize -= offset;
|
|
//readchunks
|
|
unsigned char *data = new unsigned char[dataSize];
|
|
stream->read(data, dataSize);
|
|
|
|
unsigned char *dataCur = data;
|
|
|
|
while (dataCur - data < dataSize - 2)
|
|
{
|
|
std::vector<unsigned char> arrChunk;
|
|
_UINT16 header = *((_UINT32*)dataCur); dataCur += 2;
|
|
|
|
bool bCompressed = ((header & CHUNK_COMPRESSED) != 0);
|
|
_UINT16 chunkSize = (header & CHUNK_LENMASK) + 1;
|
|
|
|
bool bUnknown = false;
|
|
if ((header & CHUNK_SIGMASK) != CHUNK_SIG)
|
|
{
|
|
bCompressed = true;
|
|
chunkSize = 4094; //по факту
|
|
bUnknown = true;
|
|
}
|
|
unsigned char *dataNext = dataCur + chunkSize;
|
|
|
|
if (bCompressed)
|
|
{
|
|
unsigned char nBitCount = 4;
|
|
_UINT16 chunkPos = 0;
|
|
|
|
bool bEof = stream->eof();
|
|
while (!bEof && ((dataCur - data) > 0) && (chunkPos < chunkSize))
|
|
{
|
|
unsigned char nToken = *dataCur; dataCur++;
|
|
++chunkPos;
|
|
|
|
for (int bit = 0; !bEof && ((dataCur - data) > 0) && (bit < 8) && (chunkPos < chunkSize); ++bit, nToken >>= 1)
|
|
{
|
|
if (nToken & 1)
|
|
{
|
|
_UINT16 nCopyToken = *((_UINT32*)dataCur); dataCur += 2;
|
|
chunkPos += 2;
|
|
|
|
while ((static_cast<size_t>(1) << nBitCount) < arrChunk.size())
|
|
++nBitCount;
|
|
|
|
_UINT16 nLength = extract(nCopyToken, 0, 16 - nBitCount) + 3;
|
|
_UINT16 nOffset = extract(nCopyToken, 16 - nBitCount, nBitCount) + 1;
|
|
|
|
bEof = (nOffset > arrChunk.size()) || (arrChunk.size() + nLength > 4096);
|
|
|
|
if (!bEof)
|
|
{
|
|
arrChunk.resize(arrChunk.size() + nLength);
|
|
|
|
unsigned char* pnTo = &*(arrChunk.end() - nLength);
|
|
|
|
const unsigned char* pnEnd = pnTo + nLength;
|
|
const unsigned char* pnFrom = pnTo - nOffset;
|
|
|
|
size_t nRunLen = (std::min)(nLength, nOffset);
|
|
while (pnTo < pnEnd)
|
|
{
|
|
size_t nStepLen = (std::min)(nRunLen, (size_t)(pnEnd - pnTo));
|
|
memcpy(pnTo, pnFrom, nStepLen);
|
|
pnTo += nStepLen;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arrChunk.push_back(*dataCur); dataCur++;
|
|
++chunkPos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arrChunk.resize(chunkSize);
|
|
memcpy(arrChunk.data(), dataCur, chunkSize); dataCur += chunkSize;
|
|
}
|
|
|
|
dataCur = dataNext;
|
|
arrChunks.insert(arrChunks.end(), arrChunk.begin(), arrChunk.end());
|
|
}
|
|
}
|
|
CVbaFileStream::~CVbaFileStream()
|
|
{
|
|
arrChunks.clear();
|
|
}
|
|
void CVbaFileStream::read(void* buf, size_t size)
|
|
{
|
|
if (NULL == buf || arrChunks.empty())
|
|
{
|
|
return;
|
|
}
|
|
if (pos + size > arrChunks.size())
|
|
size = arrChunks.size() - pos;
|
|
memcpy(buf, arrChunks.data() + pos, size);
|
|
pos += size;
|
|
|
|
}
|