Files
Aaru.Compression.Native/3rdparty/lzma/CPP/7zip/Archive/7z/7zExtract.cpp

446 lines
10 KiB
C++
Raw Normal View History

2021-10-19 21:27:23 +01:00
// 7zExtract.cpp
#include "StdAfx.h"
#include "../../../../C/7zCrc.h"
#include "../../../Common/ComTry.h"
#include "../../Common/ProgressUtils.h"
#include "7zDecode.h"
#include "7zHandler.h"
// EXTERN_g_ExternalCodecs
namespace NArchive {
namespace N7z {
2023-09-24 03:13:03 +01:00
Z7_CLASS_IMP_COM_1(
CFolderOutStream
, ISequentialOutStream
/* , ICompressGetSubStreamSize */
)
2021-10-19 21:27:23 +01:00
CMyComPtr<ISequentialOutStream> _stream;
public:
bool TestMode;
bool CheckCrc;
private:
bool _fileIsOpen;
bool _calcCrc;
UInt32 _crc;
UInt64 _rem;
const UInt32 *_indexes;
2023-09-24 03:13:03 +01:00
// unsigned _startIndex;
2021-10-19 21:27:23 +01:00
unsigned _numFiles;
unsigned _fileIndex;
HRESULT OpenFile(bool isCorrupted = false);
HRESULT CloseFile_and_SetResult(Int32 res);
HRESULT CloseFile();
HRESULT ProcessEmptyFiles();
public:
const CDbEx *_db;
CMyComPtr<IArchiveExtractCallback> ExtractCallback;
bool ExtraWriteWasCut;
CFolderOutStream():
TestMode(false),
CheckCrc(true)
{}
HRESULT Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles);
HRESULT FlushCorrupted(Int32 callbackOperationResult);
bool WasWritingFinished() const { return _numFiles == 0; }
};
HRESULT CFolderOutStream::Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles)
{
2023-09-24 03:13:03 +01:00
// _startIndex = startIndex;
2021-10-19 21:27:23 +01:00
_fileIndex = startIndex;
_indexes = indexes;
_numFiles = numFiles;
_fileIsOpen = false;
ExtraWriteWasCut = false;
return ProcessEmptyFiles();
}
HRESULT CFolderOutStream::OpenFile(bool isCorrupted)
{
const CFileItem &fi = _db->Files[_fileIndex];
2023-09-24 03:13:03 +01:00
const UInt32 nextFileIndex = (_indexes ? *_indexes : _fileIndex);
Int32 askMode = (_fileIndex == nextFileIndex) ? TestMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract :
2021-10-19 21:27:23 +01:00
NExtract::NAskMode::kSkip;
if (isCorrupted
&& askMode == NExtract::NAskMode::kExtract
&& !_db->IsItemAnti(_fileIndex)
&& !fi.IsDir)
askMode = NExtract::NAskMode::kTest;
CMyComPtr<ISequentialOutStream> realOutStream;
2023-09-24 03:13:03 +01:00
RINOK(ExtractCallback->GetStream(_fileIndex, &realOutStream, askMode))
2021-10-19 21:27:23 +01:00
_stream = realOutStream;
_crc = CRC_INIT_VAL;
_calcCrc = (CheckCrc && fi.CrcDefined && !fi.IsDir);
_fileIsOpen = true;
_rem = fi.Size;
if (askMode == NExtract::NAskMode::kExtract
&& !realOutStream
&& !_db->IsItemAnti(_fileIndex)
&& !fi.IsDir)
askMode = NExtract::NAskMode::kSkip;
return ExtractCallback->PrepareOperation(askMode);
}
HRESULT CFolderOutStream::CloseFile_and_SetResult(Int32 res)
{
_stream.Release();
_fileIsOpen = false;
if (!_indexes)
_numFiles--;
else if (*_indexes == _fileIndex)
{
_indexes++;
_numFiles--;
}
_fileIndex++;
return ExtractCallback->SetOperationResult(res);
}
HRESULT CFolderOutStream::CloseFile()
{
const CFileItem &fi = _db->Files[_fileIndex];
return CloseFile_and_SetResult((!_calcCrc || fi.Crc == CRC_GET_DIGEST(_crc)) ?
NExtract::NOperationResult::kOK :
NExtract::NOperationResult::kCRCError);
}
HRESULT CFolderOutStream::ProcessEmptyFiles()
{
while (_numFiles != 0 && _db->Files[_fileIndex].Size == 0)
{
2023-09-24 03:13:03 +01:00
RINOK(OpenFile())
RINOK(CloseFile())
2021-10-19 21:27:23 +01:00
}
return S_OK;
}
2023-09-24 03:13:03 +01:00
Z7_COM7F_IMF(CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize))
2021-10-19 21:27:23 +01:00
{
if (processedSize)
*processedSize = 0;
while (size != 0)
{
if (_fileIsOpen)
{
UInt32 cur = (size < _rem ? size : (UInt32)_rem);
if (_calcCrc)
{
const UInt32 k_Step = (UInt32)1 << 20;
if (cur > k_Step)
cur = k_Step;
}
HRESULT result = S_OK;
if (_stream)
result = _stream->Write(data, cur, &cur);
if (_calcCrc)
_crc = CrcUpdate(_crc, data, cur);
if (processedSize)
*processedSize += cur;
data = (const Byte *)data + cur;
size -= cur;
_rem -= cur;
if (_rem == 0)
{
2023-09-24 03:13:03 +01:00
RINOK(CloseFile())
RINOK(ProcessEmptyFiles())
2021-10-19 21:27:23 +01:00
}
2023-09-24 03:13:03 +01:00
RINOK(result)
2021-10-19 21:27:23 +01:00
if (cur == 0)
break;
continue;
}
2023-09-24 03:13:03 +01:00
RINOK(ProcessEmptyFiles())
2021-10-19 21:27:23 +01:00
if (_numFiles == 0)
{
// we support partial extracting
/*
if (processedSize)
*processedSize += size;
break;
*/
ExtraWriteWasCut = true;
// return S_FALSE;
return k_My_HRESULT_WritingWasCut;
}
2023-09-24 03:13:03 +01:00
RINOK(OpenFile())
2021-10-19 21:27:23 +01:00
}
return S_OK;
}
HRESULT CFolderOutStream::FlushCorrupted(Int32 callbackOperationResult)
{
while (_numFiles != 0)
{
if (_fileIsOpen)
{
2023-09-24 03:13:03 +01:00
RINOK(CloseFile_and_SetResult(callbackOperationResult))
2021-10-19 21:27:23 +01:00
}
else
{
2023-09-24 03:13:03 +01:00
RINOK(OpenFile(true))
2021-10-19 21:27:23 +01:00
}
}
return S_OK;
}
2023-09-24 03:13:03 +01:00
/*
Z7_COM7F_IMF(CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value))
{
*value = 0;
// const unsigned numFiles_Original = _numFiles + _fileIndex - _startIndex;
const unsigned numFiles_Original = _numFiles;
if (subStream >= numFiles_Original)
return S_FALSE; // E_FAIL;
*value = _db->Files[_startIndex + (unsigned)subStream].Size;
return S_OK;
}
*/
Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec))
2021-10-19 21:27:23 +01:00
{
// for GCC
// CFolderOutStream *folderOutStream = new CFolderOutStream;
// CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
COM_TRY_BEGIN
CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec;
UInt64 importantTotalUnpacked = 0;
// numItems = (UInt32)(Int32)-1;
2023-09-24 03:13:03 +01:00
const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2021-10-19 21:27:23 +01:00
if (allFilesMode)
numItems = _db.Files.Size();
if (numItems == 0)
return S_OK;
{
CNum prevFolder = kNumNoIndex;
UInt32 nextFile = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
{
2023-09-24 03:13:03 +01:00
const UInt32 fileIndex = allFilesMode ? i : indices[i];
const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
2021-10-19 21:27:23 +01:00
if (folderIndex == kNumNoIndex)
continue;
if (folderIndex != prevFolder || fileIndex < nextFile)
nextFile = _db.FolderStartFileIndex[folderIndex];
for (CNum index = nextFile; index <= fileIndex; index++)
importantTotalUnpacked += _db.Files[index].Size;
nextFile = fileIndex + 1;
prevFolder = folderIndex;
}
}
2023-09-24 03:13:03 +01:00
RINOK(extractCallback->SetTotal(importantTotalUnpacked))
2021-10-19 21:27:23 +01:00
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(extractCallback, false);
CDecoder decoder(
#if !defined(USE_MIXER_MT)
false
#elif !defined(USE_MIXER_ST)
true
2023-09-24 03:13:03 +01:00
#elif !defined(Z7_7Z_SET_PROPERTIES)
#ifdef Z7_ST
2021-10-19 21:27:23 +01:00
false
#else
true
#endif
#else
_useMultiThreadMixer
#endif
);
UInt64 curPacked, curUnpacked;
2023-09-24 03:13:03 +01:00
CMyComPtr<IArchiveExtractCallbackMessage2> callbackMessage;
extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage2, &callbackMessage);
2021-10-19 21:27:23 +01:00
CFolderOutStream *folderOutStream = new CFolderOutStream;
CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
folderOutStream->_db = &_db;
folderOutStream->ExtractCallback = extractCallback;
folderOutStream->TestMode = (testModeSpec != 0);
folderOutStream->CheckCrc = (_crcSize != 0);
for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked)
{
2023-09-24 03:13:03 +01:00
RINOK(lps->SetCur())
2021-10-19 21:27:23 +01:00
if (i >= numItems)
break;
curUnpacked = 0;
curPacked = 0;
UInt32 fileIndex = allFilesMode ? i : indices[i];
2023-09-24 03:13:03 +01:00
const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
2021-10-19 21:27:23 +01:00
UInt32 numSolidFiles = 1;
if (folderIndex != kNumNoIndex)
{
curPacked = _db.GetFolderFullPackSize(folderIndex);
UInt32 nextFile = fileIndex + 1;
fileIndex = _db.FolderStartFileIndex[folderIndex];
UInt32 k;
for (k = i + 1; k < numItems; k++)
{
2023-09-24 03:13:03 +01:00
const UInt32 fileIndex2 = allFilesMode ? k : indices[k];
2021-10-19 21:27:23 +01:00
if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex
|| fileIndex2 < nextFile)
break;
nextFile = fileIndex2 + 1;
}
numSolidFiles = k - i;
for (k = fileIndex; k < nextFile; k++)
curUnpacked += _db.Files[k].Size;
}
{
2023-09-24 03:13:03 +01:00
const HRESULT result = folderOutStream->Init(fileIndex,
2021-10-19 21:27:23 +01:00
allFilesMode ? NULL : indices + i,
numSolidFiles);
i += numSolidFiles;
2023-09-24 03:13:03 +01:00
RINOK(result)
2021-10-19 21:27:23 +01:00
}
if (folderOutStream->WasWritingFinished())
2023-09-24 03:13:03 +01:00
{
// for debug: to test zero size stream unpacking
// if (folderIndex == kNumNoIndex) // enable this check for debug
2021-10-19 21:27:23 +01:00
continue;
2023-09-24 03:13:03 +01:00
}
if (folderIndex == kNumNoIndex)
return E_FAIL;
2021-10-19 21:27:23 +01:00
2023-09-24 03:13:03 +01:00
#ifndef Z7_NO_CRYPTO
2021-10-19 21:27:23 +01:00
CMyComPtr<ICryptoGetTextPassword> getTextPassword;
if (extractCallback)
extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
#endif
try
{
2023-09-24 03:13:03 +01:00
#ifndef Z7_NO_CRYPTO
2021-10-19 21:27:23 +01:00
bool isEncrypted = false;
bool passwordIsDefined = false;
UString_Wipe password;
#endif
bool dataAfterEnd_Error = false;
2023-09-24 03:13:03 +01:00
const HRESULT result = decoder.Decode(
2021-10-19 21:27:23 +01:00
EXTERNAL_CODECS_VARS
_inStream,
_db.ArcInfo.DataStartPosition,
_db, folderIndex,
&curUnpacked,
outStream,
progress,
NULL // *inStreamMainRes
, dataAfterEnd_Error
2023-09-24 03:13:03 +01:00
Z7_7Z_DECODER_CRYPRO_VARS
#if !defined(Z7_ST)
, true, _numThreads, _memUsage_Decompress
2021-10-19 21:27:23 +01:00
#endif
);
if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error)
{
2023-09-24 03:13:03 +01:00
const bool wasFinished = folderOutStream->WasWritingFinished();
2021-10-19 21:27:23 +01:00
int resOp = NExtract::NOperationResult::kDataError;
if (result != S_FALSE)
{
if (result == E_NOTIMPL)
resOp = NExtract::NOperationResult::kUnsupportedMethod;
else if (wasFinished && dataAfterEnd_Error)
resOp = NExtract::NOperationResult::kDataAfterEnd;
}
2023-09-24 03:13:03 +01:00
RINOK(folderOutStream->FlushCorrupted(resOp))
2021-10-19 21:27:23 +01:00
if (wasFinished)
{
// we don't show error, if it's after required files
if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage)
{
2023-09-24 03:13:03 +01:00
RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp))
2021-10-19 21:27:23 +01:00
}
}
continue;
}
if (result != S_OK)
return result;
2023-09-24 03:13:03 +01:00
RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError))
2021-10-19 21:27:23 +01:00
continue;
}
catch(...)
{
2023-09-24 03:13:03 +01:00
RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError))
2021-10-19 21:27:23 +01:00
// continue;
// return E_FAIL;
throw;
}
}
return S_OK;
COM_TRY_END
}
}}