1// 7zExtract.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/ComTry.h"
6
7#include "../../Common/ProgressUtils.h"
8
9#include "7zDecode.h"
10// #include "7z1Decode.h"
11#include "7zFolderOutStream.h"
12#include "7zHandler.h"
13
14namespace NArchive {
15namespace N7z {
16
17struct CExtractFolderInfo
18{
19  #ifdef _7Z_VOL
20  int VolumeIndex;
21  #endif
22  CNum FileIndex;
23  CNum FolderIndex;
24  CBoolVector ExtractStatuses;
25  UInt64 UnpackSize;
26  CExtractFolderInfo(
27    #ifdef _7Z_VOL
28    int volumeIndex,
29    #endif
30    CNum fileIndex, CNum folderIndex):
31    #ifdef _7Z_VOL
32    VolumeIndex(volumeIndex),
33    #endif
34    FileIndex(fileIndex),
35    FolderIndex(folderIndex),
36    UnpackSize(0)
37  {
38    if (fileIndex != kNumNoIndex)
39    {
40      ExtractStatuses.Reserve(1);
41      ExtractStatuses.Add(true);
42    }
43  };
44};
45
46STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
47    Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec)
48{
49  COM_TRY_BEGIN
50  bool testMode = (testModeSpec != 0);
51  CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec;
52  UInt64 importantTotalUnpacked = 0;
53
54  bool allFilesMode = (numItems == (UInt32)-1);
55  if (allFilesMode)
56    numItems =
57    #ifdef _7Z_VOL
58    _refs.Size();
59    #else
60    _db.Files.Size();
61    #endif
62
63  if(numItems == 0)
64    return S_OK;
65
66  /*
67  if(_volumes.Size() != 1)
68    return E_FAIL;
69  const CVolume &volume = _volumes.Front();
70  const CArchiveDatabaseEx &_db = volume.Database;
71  IInStream *_inStream = volume.Stream;
72  */
73
74  CObjectVector<CExtractFolderInfo> extractFolderInfoVector;
75  for (UInt32 ii = 0; ii < numItems; ii++)
76  {
77    // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex];
78    UInt32 ref2Index = allFilesMode ? ii : indices[ii];
79    // const CRef2 &ref2 = _refs[ref2Index];
80
81    // for (UInt32 ri = 0; ri < ref2.Refs.Size(); ri++)
82    {
83      #ifdef _7Z_VOL
84      // const CRef &ref = ref2.Refs[ri];
85      const CRef &ref = _refs[ref2Index];
86
87      int volumeIndex = ref.VolumeIndex;
88      const CVolume &volume = _volumes[volumeIndex];
89      const CArchiveDatabaseEx &db = volume.Database;
90      UInt32 fileIndex = ref.ItemIndex;
91      #else
92      const CArchiveDatabaseEx &db = _db;
93      UInt32 fileIndex = ref2Index;
94      #endif
95
96      CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex];
97      if (folderIndex == kNumNoIndex)
98      {
99        extractFolderInfoVector.Add(CExtractFolderInfo(
100            #ifdef _7Z_VOL
101            volumeIndex,
102            #endif
103            fileIndex, kNumNoIndex));
104        continue;
105      }
106      if (extractFolderInfoVector.IsEmpty() ||
107        folderIndex != extractFolderInfoVector.Back().FolderIndex
108        #ifdef _7Z_VOL
109        || volumeIndex != extractFolderInfoVector.Back().VolumeIndex
110        #endif
111        )
112      {
113        extractFolderInfoVector.Add(CExtractFolderInfo(
114            #ifdef _7Z_VOL
115            volumeIndex,
116            #endif
117            kNumNoIndex, folderIndex));
118        const CFolder &folderInfo = db.Folders[folderIndex];
119        UInt64 unpackSize = folderInfo.GetUnpackSize();
120        importantTotalUnpacked += unpackSize;
121        extractFolderInfoVector.Back().UnpackSize = unpackSize;
122      }
123
124      CExtractFolderInfo &efi = extractFolderInfoVector.Back();
125
126      // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex];
127      CNum startIndex = db.FolderStartFileIndex[folderIndex];
128      for (CNum index = efi.ExtractStatuses.Size();
129          index <= fileIndex - startIndex; index++)
130      {
131        // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize;
132        // Count partial_folder_size
133        // efi.UnpackSize += unpackSize;
134        // importantTotalUnpacked += unpackSize;
135        efi.ExtractStatuses.Add(index == fileIndex - startIndex);
136      }
137    }
138  }
139
140  RINOK(extractCallback->SetTotal(importantTotalUnpacked));
141
142  CDecoder decoder(
143    #ifdef _ST_MODE
144    false
145    #else
146    true
147    #endif
148    );
149  // CDecoder1 decoder;
150
151  UInt64 totalPacked = 0;
152  UInt64 totalUnpacked = 0;
153  UInt64 curPacked, curUnpacked;
154
155  CLocalProgress *lps = new CLocalProgress;
156  CMyComPtr<ICompressProgressInfo> progress = lps;
157  lps->Init(extractCallback, false);
158
159  for (int i = 0;; i++, totalUnpacked += curUnpacked, totalPacked += curPacked)
160  {
161    lps->OutSize = totalUnpacked;
162    lps->InSize = totalPacked;
163    RINOK(lps->SetCur());
164
165    if (i >= extractFolderInfoVector.Size())
166      break;
167
168    const CExtractFolderInfo &efi = extractFolderInfoVector[i];
169    curUnpacked = efi.UnpackSize;
170    curPacked = 0;
171
172    CFolderOutStream *folderOutStream = new CFolderOutStream;
173    CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
174
175    #ifdef _7Z_VOL
176    const CVolume &volume = _volumes[efi.VolumeIndex];
177    const CArchiveDatabaseEx &db = volume.Database;
178    #else
179    const CArchiveDatabaseEx &db = _db;
180    #endif
181
182    CNum startIndex;
183    if (efi.FileIndex != kNumNoIndex)
184      startIndex = efi.FileIndex;
185    else
186      startIndex = db.FolderStartFileIndex[efi.FolderIndex];
187
188    HRESULT result = folderOutStream->Init(&db,
189        #ifdef _7Z_VOL
190        volume.StartRef2Index,
191        #else
192        0,
193        #endif
194        startIndex,
195        &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0);
196
197    RINOK(result);
198
199    if (efi.FileIndex != kNumNoIndex)
200      continue;
201
202    CNum folderIndex = efi.FolderIndex;
203    const CFolder &folderInfo = db.Folders[folderIndex];
204
205    curPacked = _db.GetFolderFullPackSize(folderIndex);
206
207    CNum packStreamIndex = db.FolderStartPackStreamIndex[folderIndex];
208    UInt64 folderStartPackPos = db.GetFolderStreamPos(folderIndex, 0);
209
210    #ifndef _NO_CRYPTO
211    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
212    if (extractCallback)
213      extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
214    #endif
215
216    try
217    {
218      #ifndef _NO_CRYPTO
219      bool passwordIsDefined;
220      #endif
221
222      HRESULT result = decoder.Decode(
223          EXTERNAL_CODECS_VARS
224          #ifdef _7Z_VOL
225          volume.Stream,
226          #else
227          _inStream,
228          #endif
229          folderStartPackPos,
230          &db.PackSizes[packStreamIndex],
231          folderInfo,
232          outStream,
233          progress
234          #ifndef _NO_CRYPTO
235          , getTextPassword, passwordIsDefined
236          #endif
237          #if !defined(_7ZIP_ST) && !defined(_SFX)
238          , true, _numThreads
239          #endif
240          );
241
242      if (result == S_FALSE)
243      {
244        RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
245        continue;
246      }
247      if (result == E_NOTIMPL)
248      {
249        RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kUnSupportedMethod));
250        continue;
251      }
252      if (result != S_OK)
253        return result;
254      if (folderOutStream->WasWritingFinished() != S_OK)
255      {
256        RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
257        continue;
258      }
259    }
260    catch(...)
261    {
262      RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
263      continue;
264    }
265  }
266  return S_OK;
267  COM_TRY_END
268}
269
270}}
271