1// Extract.cpp
2
3#include "StdAfx.h"
4
5#include <stdio.h>
6
7#include "Windows/FileDir.h"
8#include "Windows/PropVariant.h"
9#include "Windows/PropVariantConversions.h"
10
11#include "../Common/ExtractingFilePath.h"
12
13#include "Extract.h"
14#include "SetProperties.h"
15
16using namespace NWindows;
17
18static HRESULT DecompressArchive(
19    const CArc &arc,
20    UInt64 packSize,
21    const NWildcard::CCensorNode &wildcardCensor,
22    const CExtractOptions &options,
23    IExtractCallbackUI *callback,
24    CArchiveExtractCallback *extractCallbackSpec,
25    UString &errorMessage,
26    UInt64 &stdInProcessed)
27{
28  stdInProcessed = 0;
29  IInArchive *archive = arc.Archive;
30  CRecordVector<UInt32> realIndices;
31  if (!options.StdInMode)
32  {
33    UInt32 numItems;
34    RINOK(archive->GetNumberOfItems(&numItems));
35
36    for (UInt32 i = 0; i < numItems; i++)
37    {
38      UString filePath;
39      RINOK(arc.GetItemPath(i, filePath));
40      bool isFolder;
41      RINOK(IsArchiveItemFolder(archive, i, isFolder));
42      if (!wildcardCensor.CheckPath(filePath, !isFolder))
43        continue;
44      realIndices.Add(i);
45    }
46    if (realIndices.Size() == 0)
47    {
48      callback->ThereAreNoFiles();
49      return S_OK;
50    }
51  }
52
53  UStringVector removePathParts;
54
55  UString outDir = options.OutputDir;
56  outDir.Replace(L"*", GetCorrectFsPath(arc.DefaultName));
57  #ifdef _WIN32
58  // GetCorrectFullFsPath doesn't like "..".
59  // outDir.TrimRight();
60  // outDir = GetCorrectFullFsPath(outDir);
61  #endif
62
63  if (!outDir.IsEmpty())
64    if (!NFile::NDirectory::CreateComplexDirectory(outDir))
65    {
66      HRESULT res = ::GetLastError();
67      if (res == S_OK)
68        res = E_FAIL;
69      errorMessage = ((UString)L"Can not create output directory ") + outDir;
70      return res;
71    }
72
73  extractCallbackSpec->Init(
74      options.StdInMode ? &wildcardCensor : NULL,
75      &arc,
76      callback,
77      options.StdOutMode, options.TestMode, options.CalcCrc,
78      outDir,
79      removePathParts,
80      packSize);
81
82  #if !defined(_7ZIP_ST) && !defined(_SFX)
83  RINOK(SetProperties(archive, options.Properties));
84  #endif
85
86  HRESULT result;
87  Int32 testMode = (options.TestMode && !options.CalcCrc) ? 1: 0;
88  if (options.StdInMode)
89  {
90    result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, extractCallbackSpec);
91    NCOM::CPropVariant prop;
92    if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
93      if (prop.vt == VT_UI8 || prop.vt == VT_UI4)
94        stdInProcessed = ConvertPropVariantToUInt64(prop);
95  }
96  else
97    result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallbackSpec);
98
99  return callback->ExtractResult(result);
100}
101
102HRESULT DecompressArchives(
103    CCodecs *codecs, const CIntVector &formatIndices,
104    UStringVector &arcPaths, UStringVector &arcPathsFull,
105    const NWildcard::CCensorNode &wildcardCensor,
106    const CExtractOptions &options,
107    IOpenCallbackUI *openCallback,
108    IExtractCallbackUI *extractCallback,
109    UString &errorMessage,
110    CDecompressStat &stat)
111{
112  stat.Clear();
113  int i;
114  UInt64 totalPackSize = 0;
115  CRecordVector<UInt64> archiveSizes;
116
117  int numArcs = options.StdInMode ? 1 : arcPaths.Size();
118
119  for (i = 0; i < numArcs; i++)
120  {
121    NFile::NFind::CFileInfoW fi;
122    fi.Size = 0;
123    if (!options.StdInMode)
124    {
125      const UString &arcPath = arcPaths[i];
126      if (!fi.Find(arcPath))
127        throw "there is no such archive";
128      if (fi.IsDir())
129        throw "can't decompress folder";
130    }
131    archiveSizes.Add(fi.Size);
132    totalPackSize += fi.Size;
133  }
134  CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
135  CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec);
136  bool multi = (numArcs > 1);
137  extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode);
138  if (multi)
139  {
140    RINOK(extractCallback->SetTotal(totalPackSize));
141  }
142  for (i = 0; i < numArcs; i++)
143  {
144    const UString &arcPath = arcPaths[i];
145    NFile::NFind::CFileInfoW fi;
146    if (options.StdInMode)
147    {
148      fi.Size = 0;
149      fi.Attrib = 0;
150    }
151    else
152    {
153      if (!fi.Find(arcPath) || fi.IsDir())
154        throw "there is no such archive";
155    }
156
157    #ifndef _NO_CRYPTO
158    openCallback->Open_ClearPasswordWasAskedFlag();
159    #endif
160
161    RINOK(extractCallback->BeforeOpen(arcPath));
162    CArchiveLink archiveLink;
163
164    CIntVector formatIndices2 = formatIndices;
165    #ifndef _SFX
166    if (formatIndices.IsEmpty())
167    {
168      int pos = arcPath.ReverseFind(L'.');
169      if (pos >= 0)
170      {
171        UString s = arcPath.Mid(pos + 1);
172        int index = codecs->FindFormatForExtension(s);
173        if (index >= 0 && s == L"001")
174        {
175          s = arcPath.Left(pos);
176          pos = s.ReverseFind(L'.');
177          if (pos >= 0)
178          {
179            int index2 = codecs->FindFormatForExtension(s.Mid(pos + 1));
180            if (index2 >= 0 && s.CompareNoCase(L"rar") != 0)
181            {
182              formatIndices2.Add(index2);
183              formatIndices2.Add(index);
184            }
185          }
186        }
187      }
188    }
189    #endif
190    HRESULT result = archiveLink.Open2(codecs, formatIndices2, options.StdInMode, NULL, arcPath, openCallback);
191    if (result == E_ABORT)
192      return result;
193
194    bool crypted = false;
195    #ifndef _NO_CRYPTO
196    crypted = openCallback->Open_WasPasswordAsked();
197    #endif
198
199    RINOK(extractCallback->OpenResult(arcPath, result, crypted));
200    if (result != S_OK)
201      continue;
202
203    if (!options.StdInMode)
204    for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
205    {
206      int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]);
207      if (index >= 0 && index > i)
208      {
209        arcPaths.Delete(index);
210        arcPathsFull.Delete(index);
211        totalPackSize -= archiveSizes[index];
212        archiveSizes.Delete(index);
213        numArcs = arcPaths.Size();
214      }
215    }
216    if (archiveLink.VolumePaths.Size() != 0)
217    {
218      totalPackSize += archiveLink.VolumesSize;
219      RINOK(extractCallback->SetTotal(totalPackSize));
220    }
221
222    #ifndef _NO_CRYPTO
223    UString password;
224    RINOK(openCallback->Open_GetPasswordIfAny(password));
225    if (!password.IsEmpty())
226    {
227      RINOK(extractCallback->SetPassword(password));
228    }
229    #endif
230
231    for (int v = 0; v < archiveLink.Arcs.Size(); v++)
232    {
233      const UString &s = archiveLink.Arcs[v].ErrorMessage;
234      if (!s.IsEmpty())
235      {
236        RINOK(extractCallback->MessageError(s));
237      }
238    }
239
240    CArc &arc = archiveLink.Arcs.Back();
241    arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
242    arc.MTime = fi.MTime;
243
244    UInt64 packProcessed;
245    RINOK(DecompressArchive(arc,
246        fi.Size + archiveLink.VolumesSize,
247        wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage, packProcessed));
248    if (!options.StdInMode)
249      packProcessed = fi.Size + archiveLink.VolumesSize;
250    extractCallbackSpec->LocalProgressSpec->InSize += packProcessed;
251    extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize;
252    if (!errorMessage.IsEmpty())
253      return E_FAIL;
254  }
255  stat.NumFolders = extractCallbackSpec->NumFolders;
256  stat.NumFiles = extractCallbackSpec->NumFiles;
257  stat.UnpackSize = extractCallbackSpec->UnpackSize;
258  stat.CrcSum = extractCallbackSpec->CrcSum;
259
260  stat.NumArchives = arcPaths.Size();
261  stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize;
262  return S_OK;
263}
264