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
16 using namespace NWindows;
17
DecompressArchive(const CArc & arc,UInt64 packSize,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,IExtractCallbackUI * callback,CArchiveExtractCallback * extractCallbackSpec,UString & errorMessage,UInt64 & stdInProcessed)18 static 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
DecompressArchives(CCodecs * codecs,const CIntVector & formatIndices,UStringVector & arcPaths,UStringVector & arcPathsFull,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,IOpenCallbackUI * openCallback,IExtractCallbackUI * extractCallback,UString & errorMessage,CDecompressStat & stat)102 HRESULT 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