|
1 // OpenArchive.cpp |
|
2 |
|
3 #include "StdAfx.h" |
|
4 |
|
5 #include "OpenArchive.h" |
|
6 |
|
7 #include "Common/Wildcard.h" |
|
8 |
|
9 #include "Windows/FileName.h" |
|
10 #include "Windows/FileDir.h" |
|
11 #include "Windows/Defs.h" |
|
12 #include "Windows/PropVariant.h" |
|
13 |
|
14 #include "../../Common/FileStreams.h" |
|
15 #include "../../Common/StreamUtils.h" |
|
16 |
|
17 #include "Common/StringConvert.h" |
|
18 |
|
19 #ifdef FORMAT_7Z |
|
20 #include "../../Archive/7z/7zHandler.h" |
|
21 #endif |
|
22 |
|
23 #ifdef FORMAT_BZIP2 |
|
24 #include "../../Archive/BZip2/BZip2Handler.h" |
|
25 #endif |
|
26 |
|
27 #ifdef FORMAT_GZIP |
|
28 #include "../../Archive/GZip/GZipHandler.h" |
|
29 #endif |
|
30 |
|
31 #ifdef FORMAT_SPLIT |
|
32 #include "../../Archive/Split/SplitHandler.h" |
|
33 #endif |
|
34 |
|
35 #ifdef FORMAT_TAR |
|
36 #include "../../Archive/Tar/TarHandler.h" |
|
37 #endif |
|
38 |
|
39 #ifdef FORMAT_ZIP |
|
40 #include "../../Archive/Zip/ZipHandler.h" |
|
41 #endif |
|
42 |
|
43 #ifdef FORMAT_Z |
|
44 #include "../../Archive/Z/ZHandler.h" |
|
45 #endif |
|
46 |
|
47 #ifndef EXCLUDE_COM |
|
48 #include "HandlerLoader.h" |
|
49 #endif |
|
50 |
|
51 #include "DefaultName.h" |
|
52 |
|
53 using namespace NWindows; |
|
54 |
|
55 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result) |
|
56 { |
|
57 NCOM::CPropVariant prop; |
|
58 RINOK(archive->GetProperty(index, kpidPath, &prop)); |
|
59 if(prop.vt == VT_BSTR) |
|
60 result = prop.bstrVal; |
|
61 else if (prop.vt == VT_EMPTY) |
|
62 result.Empty(); |
|
63 else |
|
64 return E_FAIL; |
|
65 return S_OK; |
|
66 } |
|
67 |
|
68 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result) |
|
69 { |
|
70 RINOK(GetArchiveItemPath(archive, index, result)); |
|
71 if (result.IsEmpty()) |
|
72 result = defaultName; |
|
73 return S_OK; |
|
74 } |
|
75 |
|
76 HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index, |
|
77 const FILETIME &defaultFileTime, FILETIME &fileTime) |
|
78 { |
|
79 NCOM::CPropVariant prop; |
|
80 RINOK(archive->GetProperty(index, kpidLastWriteTime, &prop)); |
|
81 if (prop.vt == VT_FILETIME) |
|
82 fileTime = prop.filetime; |
|
83 else if (prop.vt == VT_EMPTY) |
|
84 fileTime = defaultFileTime; |
|
85 else |
|
86 return E_FAIL; |
|
87 return S_OK; |
|
88 } |
|
89 |
|
90 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) |
|
91 { |
|
92 NCOM::CPropVariant prop; |
|
93 RINOK(archive->GetProperty(index, propID, &prop)); |
|
94 if(prop.vt == VT_BOOL) |
|
95 result = VARIANT_BOOLToBool(prop.boolVal); |
|
96 else if (prop.vt == VT_EMPTY) |
|
97 result = false; |
|
98 else |
|
99 return E_FAIL; |
|
100 return S_OK; |
|
101 } |
|
102 |
|
103 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) |
|
104 { |
|
105 return IsArchiveItemProp(archive, index, kpidIsFolder, result); |
|
106 } |
|
107 |
|
108 HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result) |
|
109 { |
|
110 return IsArchiveItemProp(archive, index, kpidIsAnti, result); |
|
111 } |
|
112 |
|
113 // Static-SFX (for Linux) can be big |
|
114 const UInt64 kMaxCheckStartPosition = |
|
115 #ifdef _WIN32 |
|
116 1 << 20; |
|
117 #else |
|
118 1 << 22; |
|
119 #endif |
|
120 |
|
121 |
|
122 HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName) |
|
123 { |
|
124 CInFileStream *inStreamSpec = new CInFileStream; |
|
125 CMyComPtr<IInStream> inStream(inStreamSpec); |
|
126 inStreamSpec->Open(fileName); |
|
127 return archive->Open(inStream, &kMaxCheckStartPosition, NULL); |
|
128 } |
|
129 |
|
130 #ifndef _SFX |
|
131 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) |
|
132 { |
|
133 for (size_t i = 0; i < size; i++) |
|
134 if (p1[i] != p2[i]) |
|
135 return false; |
|
136 return true; |
|
137 } |
|
138 #endif |
|
139 |
|
140 HRESULT OpenArchive( |
|
141 IInStream *inStream, |
|
142 const UString &fileName, |
|
143 #ifndef EXCLUDE_COM |
|
144 HMODULE *module, |
|
145 #endif |
|
146 IInArchive **archiveResult, |
|
147 CArchiverInfo &archiverInfoResult, |
|
148 UString &defaultItemName, |
|
149 IArchiveOpenCallback *openArchiveCallback) |
|
150 { |
|
151 *archiveResult = NULL; |
|
152 CObjectVector<CArchiverInfo> archiverInfoList; |
|
153 ReadArchiverInfoList(archiverInfoList); |
|
154 UString extension; |
|
155 { |
|
156 int dotPos = fileName.ReverseFind(L'.'); |
|
157 if (dotPos >= 0) |
|
158 extension = fileName.Mid(dotPos + 1); |
|
159 } |
|
160 CIntVector orderIndices; |
|
161 int i; |
|
162 bool finded = false; |
|
163 for(i = 0; i < archiverInfoList.Size(); i++) |
|
164 { |
|
165 if (archiverInfoList[i].FindExtension(extension) >= 0) |
|
166 { |
|
167 orderIndices.Insert(0, i); |
|
168 finded = true; |
|
169 } |
|
170 else |
|
171 orderIndices.Add(i); |
|
172 } |
|
173 |
|
174 #ifndef _SFX |
|
175 if (!finded) |
|
176 { |
|
177 CByteBuffer byteBuffer; |
|
178 const UInt32 kBufferSize = (200 << 10); |
|
179 byteBuffer.SetCapacity(kBufferSize); |
|
180 Byte *buffer = byteBuffer; |
|
181 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); |
|
182 UInt32 processedSize; |
|
183 RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize)); |
|
184 int numFinded = 0; |
|
185 for (int pos = (int)processedSize; pos >= 0 ; pos--) |
|
186 { |
|
187 for(int i = numFinded; i < orderIndices.Size(); i++) |
|
188 { |
|
189 int index = orderIndices[i]; |
|
190 const CArchiverInfo &ai = archiverInfoList[index]; |
|
191 const CByteBuffer &sig = ai.StartSignature; |
|
192 if (sig.GetCapacity() == 0) |
|
193 continue; |
|
194 if (pos + sig.GetCapacity() > processedSize) |
|
195 continue; |
|
196 if (TestSignature(buffer + pos, sig, sig.GetCapacity())) |
|
197 { |
|
198 orderIndices.Delete(i); |
|
199 orderIndices.Insert(0, index); |
|
200 numFinded++; |
|
201 } |
|
202 } |
|
203 } |
|
204 } |
|
205 #endif |
|
206 |
|
207 HRESULT badResult = S_OK; |
|
208 for(i = 0; i < orderIndices.Size(); i++) |
|
209 { |
|
210 inStream->Seek(0, STREAM_SEEK_SET, NULL); |
|
211 const CArchiverInfo &archiverInfo = archiverInfoList[orderIndices[i]]; |
|
212 #ifndef EXCLUDE_COM |
|
213 CHandlerLoader loader; |
|
214 #endif |
|
215 CMyComPtr<IInArchive> archive; |
|
216 |
|
217 #ifdef FORMAT_7Z |
|
218 if (archiverInfo.Name.CompareNoCase(L"7z") == 0) |
|
219 archive = new NArchive::N7z::CHandler; |
|
220 #endif |
|
221 |
|
222 #ifdef FORMAT_BZIP2 |
|
223 if (archiverInfo.Name.CompareNoCase(L"BZip2") == 0) |
|
224 archive = new NArchive::NBZip2::CHandler; |
|
225 #endif |
|
226 |
|
227 #ifdef FORMAT_GZIP |
|
228 if (archiverInfo.Name.CompareNoCase(L"GZip") == 0) |
|
229 archive = new NArchive::NGZip::CHandler; |
|
230 #endif |
|
231 |
|
232 #ifdef FORMAT_SPLIT |
|
233 if (archiverInfo.Name.CompareNoCase(L"Split") == 0) |
|
234 archive = new NArchive::NSplit::CHandler; |
|
235 #endif |
|
236 |
|
237 #ifdef FORMAT_TAR |
|
238 if (archiverInfo.Name.CompareNoCase(L"Tar") == 0) |
|
239 archive = new NArchive::NTar::CHandler; |
|
240 #endif |
|
241 |
|
242 #ifdef FORMAT_ZIP |
|
243 if (archiverInfo.Name.CompareNoCase(L"Zip") == 0) |
|
244 archive = new NArchive::NZip::CHandler; |
|
245 #endif |
|
246 |
|
247 #ifdef FORMAT_Z |
|
248 if (archiverInfo.Name.CompareNoCase(L"Z") == 0) |
|
249 archive = new NArchive::NZ::CHandler; |
|
250 #endif |
|
251 |
|
252 |
|
253 #ifndef EXCLUDE_COM |
|
254 if (!archive) |
|
255 { |
|
256 HRESULT result = loader.CreateHandler(archiverInfo.FilePath, |
|
257 archiverInfo.ClassID, (void **)&archive, false); |
|
258 if (result != S_OK) |
|
259 continue; |
|
260 } |
|
261 #endif |
|
262 |
|
263 if (!archive) |
|
264 return E_FAIL; |
|
265 |
|
266 HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback); |
|
267 if(result == S_FALSE) |
|
268 continue; |
|
269 if(result != S_OK) |
|
270 { |
|
271 badResult = result; |
|
272 if(result == E_ABORT) |
|
273 break; |
|
274 continue; |
|
275 } |
|
276 *archiveResult = archive.Detach(); |
|
277 #ifndef EXCLUDE_COM |
|
278 *module = loader.Detach(); |
|
279 #endif |
|
280 archiverInfoResult = archiverInfo; |
|
281 int subExtIndex = archiverInfo.FindExtension(extension); |
|
282 if (subExtIndex < 0) |
|
283 subExtIndex = 0; |
|
284 defaultItemName = GetDefaultName2(fileName, |
|
285 archiverInfo.Extensions[subExtIndex].Ext, |
|
286 archiverInfo.Extensions[subExtIndex].AddExt); |
|
287 |
|
288 return S_OK; |
|
289 } |
|
290 if (badResult != S_OK) |
|
291 return badResult; |
|
292 return S_FALSE; |
|
293 } |
|
294 |
|
295 HRESULT OpenArchive(const UString &filePath, |
|
296 #ifndef EXCLUDE_COM |
|
297 HMODULE *module, |
|
298 #endif |
|
299 IInArchive **archiveResult, |
|
300 CArchiverInfo &archiverInfo, |
|
301 UString &defaultItemName, |
|
302 IArchiveOpenCallback *openArchiveCallback) |
|
303 { |
|
304 CInFileStream *inStreamSpec = new CInFileStream; |
|
305 CMyComPtr<IInStream> inStream(inStreamSpec); |
|
306 if (!inStreamSpec->Open(filePath)) |
|
307 return GetLastError(); |
|
308 return OpenArchive(inStream, ExtractFileNameFromPath(filePath), |
|
309 #ifndef EXCLUDE_COM |
|
310 module, |
|
311 #endif |
|
312 archiveResult, archiverInfo, |
|
313 defaultItemName, openArchiveCallback); |
|
314 } |
|
315 |
|
316 static void MakeDefaultName(UString &name) |
|
317 { |
|
318 int dotPos = name.ReverseFind(L'.'); |
|
319 if (dotPos < 0) |
|
320 return; |
|
321 UString ext = name.Mid(dotPos + 1); |
|
322 if (ext.IsEmpty()) |
|
323 return; |
|
324 for (int pos = 0; pos < ext.Length(); pos++) |
|
325 if (ext[pos] < L'0' || ext[pos] > L'9') |
|
326 return; |
|
327 name = name.Left(dotPos); |
|
328 } |
|
329 |
|
330 HRESULT OpenArchive(const UString &fileName, |
|
331 #ifndef EXCLUDE_COM |
|
332 HMODULE *module0, |
|
333 HMODULE *module1, |
|
334 #endif |
|
335 IInArchive **archive0, |
|
336 IInArchive **archive1, |
|
337 CArchiverInfo &archiverInfo0, |
|
338 CArchiverInfo &archiverInfo1, |
|
339 UString &defaultItemName0, |
|
340 UString &defaultItemName1, |
|
341 IArchiveOpenCallback *openArchiveCallback) |
|
342 { |
|
343 HRESULT result = OpenArchive(fileName, |
|
344 #ifndef EXCLUDE_COM |
|
345 module0, |
|
346 #endif |
|
347 archive0, archiverInfo0, defaultItemName0, openArchiveCallback); |
|
348 RINOK(result); |
|
349 CMyComPtr<IInArchiveGetStream> getStream; |
|
350 result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream); |
|
351 if (result != S_OK || getStream == 0) |
|
352 return S_OK; |
|
353 |
|
354 CMyComPtr<ISequentialInStream> subSeqStream; |
|
355 result = getStream->GetStream(0, &subSeqStream); |
|
356 if (result != S_OK) |
|
357 return S_OK; |
|
358 |
|
359 CMyComPtr<IInStream> subStream; |
|
360 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK) |
|
361 return S_OK; |
|
362 if (!subStream) |
|
363 return S_OK; |
|
364 |
|
365 UInt32 numItems; |
|
366 RINOK((*archive0)->GetNumberOfItems(&numItems)); |
|
367 if (numItems < 1) |
|
368 return S_OK; |
|
369 |
|
370 UString subPath; |
|
371 RINOK(GetArchiveItemPath(*archive0, 0, subPath)) |
|
372 if (subPath.IsEmpty()) |
|
373 { |
|
374 MakeDefaultName(defaultItemName0); |
|
375 subPath = defaultItemName0; |
|
376 if (archiverInfo0.Name.CompareNoCase(L"7z") == 0) |
|
377 { |
|
378 if (subPath.Right(3).CompareNoCase(L".7z") != 0) |
|
379 subPath += L".7z"; |
|
380 } |
|
381 } |
|
382 else |
|
383 subPath = ExtractFileNameFromPath(subPath); |
|
384 |
|
385 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; |
|
386 openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); |
|
387 if (setSubArchiveName) |
|
388 setSubArchiveName->SetSubArchiveName(subPath); |
|
389 |
|
390 result = OpenArchive(subStream, subPath, |
|
391 #ifndef EXCLUDE_COM |
|
392 module1, |
|
393 #endif |
|
394 archive1, archiverInfo1, defaultItemName1, openArchiveCallback); |
|
395 return S_OK; |
|
396 } |
|
397 |
|
398 HRESULT MyOpenArchive(const UString &archiveName, |
|
399 #ifndef EXCLUDE_COM |
|
400 HMODULE *module, |
|
401 #endif |
|
402 IInArchive **archive, |
|
403 UString &defaultItemName, |
|
404 IOpenCallbackUI *openCallbackUI) |
|
405 { |
|
406 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; |
|
407 CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec; |
|
408 openCallbackSpec->Callback = openCallbackUI; |
|
409 |
|
410 UString fullName; |
|
411 int fileNamePartStartIndex; |
|
412 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); |
|
413 openCallbackSpec->Init( |
|
414 fullName.Left(fileNamePartStartIndex), |
|
415 fullName.Mid(fileNamePartStartIndex)); |
|
416 |
|
417 CArchiverInfo archiverInfo; |
|
418 return OpenArchive(archiveName, |
|
419 #ifndef EXCLUDE_COM |
|
420 module, |
|
421 #endif |
|
422 archive, |
|
423 archiverInfo, |
|
424 defaultItemName, |
|
425 openCallback); |
|
426 } |
|
427 |
|
428 HRESULT MyOpenArchive(const UString &archiveName, |
|
429 #ifndef EXCLUDE_COM |
|
430 HMODULE *module0, |
|
431 HMODULE *module1, |
|
432 #endif |
|
433 IInArchive **archive0, |
|
434 IInArchive **archive1, |
|
435 UString &defaultItemName0, |
|
436 UString &defaultItemName1, |
|
437 UStringVector &volumePaths, |
|
438 IOpenCallbackUI *openCallbackUI) |
|
439 { |
|
440 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; |
|
441 CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec; |
|
442 openCallbackSpec->Callback = openCallbackUI; |
|
443 |
|
444 UString fullName; |
|
445 int fileNamePartStartIndex; |
|
446 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex); |
|
447 UString prefix = fullName.Left(fileNamePartStartIndex); |
|
448 UString name = fullName.Mid(fileNamePartStartIndex); |
|
449 openCallbackSpec->Init(prefix, name); |
|
450 |
|
451 CArchiverInfo archiverInfo0, archiverInfo1; |
|
452 HRESULT result = OpenArchive(archiveName, |
|
453 #ifndef EXCLUDE_COM |
|
454 module0, |
|
455 module1, |
|
456 #endif |
|
457 archive0, |
|
458 archive1, |
|
459 archiverInfo0, |
|
460 archiverInfo1, |
|
461 defaultItemName0, |
|
462 defaultItemName1, |
|
463 openCallback); |
|
464 RINOK(result); |
|
465 volumePaths.Add(prefix + name); |
|
466 for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) |
|
467 volumePaths.Add(prefix + openCallbackSpec->FileNames[i]); |
|
468 return S_OK; |
|
469 } |
|
470 |
|
471 HRESULT CArchiveLink::Close() |
|
472 { |
|
473 if (Archive1 != 0) |
|
474 RINOK(Archive1->Close()); |
|
475 if (Archive0 != 0) |
|
476 RINOK(Archive0->Close()); |
|
477 return S_OK; |
|
478 } |
|
479 |
|
480 void CArchiveLink::Release() |
|
481 { |
|
482 if (Archive1 != 0) |
|
483 Archive1.Release(); |
|
484 if (Archive0 != 0) |
|
485 Archive0.Release(); |
|
486 #ifndef EXCLUDE_COM |
|
487 Library1.Free(); |
|
488 Library0.Free(); |
|
489 #endif |
|
490 } |
|
491 |
|
492 HRESULT OpenArchive(const UString &archiveName, |
|
493 CArchiveLink &archiveLink, |
|
494 IArchiveOpenCallback *openCallback) |
|
495 { |
|
496 return OpenArchive(archiveName, |
|
497 #ifndef EXCLUDE_COM |
|
498 &archiveLink.Library0, &archiveLink.Library1, |
|
499 #endif |
|
500 &archiveLink.Archive0, &archiveLink.Archive1, |
|
501 archiveLink.ArchiverInfo0, archiveLink.ArchiverInfo1, |
|
502 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, |
|
503 openCallback); |
|
504 } |
|
505 |
|
506 HRESULT MyOpenArchive(const UString &archiveName, |
|
507 CArchiveLink &archiveLink, |
|
508 IOpenCallbackUI *openCallbackUI) |
|
509 { |
|
510 return MyOpenArchive(archiveName, |
|
511 #ifndef EXCLUDE_COM |
|
512 &archiveLink.Library0, &archiveLink.Library1, |
|
513 #endif |
|
514 &archiveLink.Archive0, &archiveLink.Archive1, |
|
515 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, |
|
516 archiveLink.VolumePaths, |
|
517 openCallbackUI); |
|
518 } |
|
519 |
|
520 HRESULT ReOpenArchive(CArchiveLink &archiveLink, |
|
521 const UString &fileName) |
|
522 { |
|
523 if (archiveLink.GetNumLevels() > 1) |
|
524 return E_NOTIMPL; |
|
525 if (archiveLink.GetNumLevels() == 0) |
|
526 return MyOpenArchive(fileName, archiveLink, 0); |
|
527 return ReOpenArchive(archiveLink.GetArchive(), fileName); |
|
528 } |