OLD | NEW |
| (Empty) |
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 #include "DefaultName.h" | |
20 | |
21 using namespace NWindows; | |
22 | |
23 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result) | |
24 { | |
25 NCOM::CPropVariant prop; | |
26 RINOK(archive->GetProperty(index, kpidPath, &prop)); | |
27 if(prop.vt == VT_BSTR) | |
28 result = prop.bstrVal; | |
29 else if (prop.vt == VT_EMPTY) | |
30 result.Empty(); | |
31 else | |
32 return E_FAIL; | |
33 return S_OK; | |
34 } | |
35 | |
36 HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &def
aultName, UString &result) | |
37 { | |
38 RINOK(GetArchiveItemPath(archive, index, result)); | |
39 if (result.IsEmpty()) | |
40 { | |
41 result = defaultName; | |
42 NCOM::CPropVariant prop; | |
43 RINOK(archive->GetProperty(index, kpidExtension, &prop)); | |
44 if (prop.vt == VT_BSTR) | |
45 { | |
46 result += L'.'; | |
47 result += prop.bstrVal; | |
48 } | |
49 else if (prop.vt != VT_EMPTY) | |
50 return E_FAIL; | |
51 } | |
52 return S_OK; | |
53 } | |
54 | |
55 HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index, | |
56 const FILETIME &defaultFileTime, FILETIME &fileTime) | |
57 { | |
58 NCOM::CPropVariant prop; | |
59 RINOK(archive->GetProperty(index, kpidMTime, &prop)); | |
60 if (prop.vt == VT_FILETIME) | |
61 fileTime = prop.filetime; | |
62 else if (prop.vt == VT_EMPTY) | |
63 fileTime = defaultFileTime; | |
64 else | |
65 return E_FAIL; | |
66 return S_OK; | |
67 } | |
68 | |
69 HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool
&result) | |
70 { | |
71 NCOM::CPropVariant prop; | |
72 RINOK(archive->GetProperty(index, propID, &prop)); | |
73 if(prop.vt == VT_BOOL) | |
74 result = VARIANT_BOOLToBool(prop.boolVal); | |
75 else if (prop.vt == VT_EMPTY) | |
76 result = false; | |
77 else | |
78 return E_FAIL; | |
79 return S_OK; | |
80 } | |
81 | |
82 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) | |
83 { | |
84 return IsArchiveItemProp(archive, index, kpidIsDir, result); | |
85 } | |
86 | |
87 HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result) | |
88 { | |
89 return IsArchiveItemProp(archive, index, kpidIsAnti, result); | |
90 } | |
91 | |
92 // Static-SFX (for Linux) can be big. | |
93 const UInt64 kMaxCheckStartPosition = 1 << 22; | |
94 | |
95 HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpen
Callback *openArchiveCallback) | |
96 { | |
97 CInFileStream *inStreamSpec = new CInFileStream; | |
98 CMyComPtr<IInStream> inStream(inStreamSpec); | |
99 inStreamSpec->Open(fileName); | |
100 return archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback); | |
101 } | |
102 | |
103 #ifndef _SFX | |
104 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) | |
105 { | |
106 for (size_t i = 0; i < size; i++) | |
107 if (p1[i] != p2[i]) | |
108 return false; | |
109 return true; | |
110 } | |
111 #endif | |
112 | |
113 HRESULT OpenArchive( | |
114 CCodecs *codecs, | |
115 int arcTypeIndex, | |
116 IInStream *inStream, | |
117 const UString &fileName, | |
118 IInArchive **archiveResult, | |
119 int &formatIndex, | |
120 UString &defaultItemName, | |
121 IArchiveOpenCallback *openArchiveCallback) | |
122 { | |
123 *archiveResult = NULL; | |
124 UString extension; | |
125 { | |
126 int dotPos = fileName.ReverseFind(L'.'); | |
127 if (dotPos >= 0) | |
128 extension = fileName.Mid(dotPos + 1); | |
129 } | |
130 CIntVector orderIndices; | |
131 if (arcTypeIndex >= 0) | |
132 orderIndices.Add(arcTypeIndex); | |
133 else | |
134 { | |
135 | |
136 int i; | |
137 int numFinded = 0; | |
138 for (i = 0; i < codecs->Formats.Size(); i++) | |
139 if (codecs->Formats[i].FindExtension(extension) >= 0) | |
140 orderIndices.Insert(numFinded++, i); | |
141 else | |
142 orderIndices.Add(i); | |
143 | |
144 #ifndef _SFX | |
145 if (numFinded != 1) | |
146 { | |
147 CIntVector orderIndices2; | |
148 CByteBuffer byteBuffer; | |
149 const size_t kBufferSize = (1 << 21); | |
150 byteBuffer.SetCapacity(kBufferSize); | |
151 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); | |
152 size_t processedSize = kBufferSize; | |
153 RINOK(ReadStream(inStream, byteBuffer, &processedSize)); | |
154 if (processedSize == 0) | |
155 return S_FALSE; | |
156 | |
157 const Byte *buf = byteBuffer; | |
158 Byte hash[1 << 16]; | |
159 memset(hash, 0xFF, 1 << 16); | |
160 Byte prevs[256]; | |
161 if (orderIndices.Size() > 255) | |
162 return S_FALSE; | |
163 int i; | |
164 for (i = 0; i < orderIndices.Size(); i++) | |
165 { | |
166 const CArcInfoEx &ai = codecs->Formats[orderIndices[i]]; | |
167 const CByteBuffer &sig = ai.StartSignature; | |
168 if (sig.GetCapacity() < 2) | |
169 continue; | |
170 UInt32 v = sig[0] | ((UInt32)sig[1] << 8); | |
171 prevs[i] = hash[v]; | |
172 hash[v] = (Byte)i; | |
173 } | |
174 | |
175 processedSize--; | |
176 for (UInt32 pos = 0; pos < processedSize; pos++) | |
177 { | |
178 for (; pos < processedSize && hash[buf[pos] | ((UInt32)buf[pos + 1] << 8)]
== 0xFF; pos++); | |
179 if (pos == processedSize) | |
180 break; | |
181 UInt32 v = buf[pos] | ((UInt32)buf[pos + 1] << 8); | |
182 Byte *ptr = &hash[v]; | |
183 int i = *ptr; | |
184 do | |
185 { | |
186 int index = orderIndices[i]; | |
187 const CArcInfoEx &ai = codecs->Formats[index]; | |
188 const CByteBuffer &sig = ai.StartSignature; | |
189 if (sig.GetCapacity() != 0 && pos + sig.GetCapacity() <= processedSize +
1) | |
190 if (TestSignature(buf + pos, sig, sig.GetCapacity())) | |
191 { | |
192 orderIndices2.Add(index); | |
193 orderIndices[i] = 0xFF; | |
194 *ptr = prevs[i]; | |
195 } | |
196 ptr = &prevs[i]; | |
197 i = *ptr; | |
198 } | |
199 while (i != 0xFF); | |
200 } | |
201 | |
202 for (i = 0; i < orderIndices.Size(); i++) | |
203 { | |
204 int val = orderIndices[i]; | |
205 if (val != 0xFF) | |
206 orderIndices2.Add(val); | |
207 } | |
208 orderIndices = orderIndices2; | |
209 | |
210 if (orderIndices.Size() >= 2) | |
211 { | |
212 int isoIndex = codecs->FindFormatForArchiveType(L"iso"); | |
213 int udfIndex = codecs->FindFormatForArchiveType(L"udf"); | |
214 int iIso = -1; | |
215 int iUdf = -1; | |
216 for (int i = 0; i < orderIndices.Size(); i++) | |
217 { | |
218 if (orderIndices[i] == isoIndex) iIso = i; | |
219 if (orderIndices[i] == udfIndex) iUdf = i; | |
220 } | |
221 if (iUdf == iIso + 1) | |
222 { | |
223 orderIndices[iUdf] = isoIndex; | |
224 orderIndices[iIso] = udfIndex; | |
225 } | |
226 } | |
227 } | |
228 else if (extension == L"000" || extension == L"001") | |
229 { | |
230 CByteBuffer byteBuffer; | |
231 const size_t kBufferSize = (1 << 10); | |
232 byteBuffer.SetCapacity(kBufferSize); | |
233 Byte *buffer = byteBuffer; | |
234 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); | |
235 size_t processedSize = kBufferSize; | |
236 RINOK(ReadStream(inStream, buffer, &processedSize)); | |
237 if (processedSize >= 16) | |
238 { | |
239 Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}; | |
240 if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[1
0] & 1) != 0) | |
241 { | |
242 for (int i = 0; i < orderIndices.Size(); i++) | |
243 { | |
244 int index = orderIndices[i]; | |
245 const CArcInfoEx &ai = codecs->Formats[index]; | |
246 if (ai.Name.CompareNoCase(L"rar") != 0) | |
247 continue; | |
248 orderIndices.Delete(i--); | |
249 orderIndices.Insert(0, index); | |
250 break; | |
251 } | |
252 } | |
253 } | |
254 } | |
255 #endif | |
256 } | |
257 | |
258 for(int i = 0; i < orderIndices.Size(); i++) | |
259 { | |
260 inStream->Seek(0, STREAM_SEEK_SET, NULL); | |
261 | |
262 CMyComPtr<IInArchive> archive; | |
263 | |
264 formatIndex = orderIndices[i]; | |
265 RINOK(codecs->CreateInArchive(formatIndex, archive)); | |
266 if (!archive) | |
267 continue; | |
268 | |
269 #ifdef EXTERNAL_CODECS | |
270 { | |
271 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; | |
272 archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCo
decsInfo); | |
273 if (setCompressCodecsInfo) | |
274 { | |
275 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); | |
276 } | |
277 } | |
278 #endif | |
279 | |
280 HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiv
eCallback); | |
281 if (result == S_FALSE) | |
282 continue; | |
283 RINOK(result); | |
284 *archiveResult = archive.Detach(); | |
285 const CArcInfoEx &format = codecs->Formats[formatIndex]; | |
286 if (format.Exts.Size() == 0) | |
287 { | |
288 defaultItemName = GetDefaultName2(fileName, L"", L""); | |
289 } | |
290 else | |
291 { | |
292 int subExtIndex = format.FindExtension(extension); | |
293 if (subExtIndex < 0) | |
294 subExtIndex = 0; | |
295 defaultItemName = GetDefaultName2(fileName, | |
296 format.Exts[subExtIndex].Ext, | |
297 format.Exts[subExtIndex].AddExt); | |
298 } | |
299 return S_OK; | |
300 } | |
301 return S_FALSE; | |
302 } | |
303 | |
304 HRESULT OpenArchive( | |
305 CCodecs *codecs, | |
306 int arcTypeIndex, | |
307 const UString &filePath, | |
308 IInArchive **archiveResult, | |
309 int &formatIndex, | |
310 UString &defaultItemName, | |
311 IArchiveOpenCallback *openArchiveCallback) | |
312 { | |
313 CInFileStream *inStreamSpec = new CInFileStream; | |
314 CMyComPtr<IInStream> inStream(inStreamSpec); | |
315 if (!inStreamSpec->Open(filePath)) | |
316 return GetLastError(); | |
317 return OpenArchive(codecs, arcTypeIndex, inStream, ExtractFileNameFromPath(fil
ePath), | |
318 archiveResult, formatIndex, | |
319 defaultItemName, openArchiveCallback); | |
320 } | |
321 | |
322 static void MakeDefaultName(UString &name) | |
323 { | |
324 int dotPos = name.ReverseFind(L'.'); | |
325 if (dotPos < 0) | |
326 return; | |
327 UString ext = name.Mid(dotPos + 1); | |
328 if (ext.IsEmpty()) | |
329 return; | |
330 for (int pos = 0; pos < ext.Length(); pos++) | |
331 if (ext[pos] < L'0' || ext[pos] > L'9') | |
332 return; | |
333 name = name.Left(dotPos); | |
334 } | |
335 | |
336 HRESULT OpenArchive( | |
337 CCodecs *codecs, | |
338 const CIntVector &formatIndices, | |
339 const UString &fileName, | |
340 IInArchive **archive0, | |
341 IInArchive **archive1, | |
342 int &formatIndex0, | |
343 int &formatIndex1, | |
344 UString &defaultItemName0, | |
345 UString &defaultItemName1, | |
346 IArchiveOpenCallback *openArchiveCallback) | |
347 { | |
348 if (formatIndices.Size() >= 3) | |
349 return E_NOTIMPL; | |
350 | |
351 int arcTypeIndex = -1; | |
352 if (formatIndices.Size() >= 1) | |
353 arcTypeIndex = formatIndices[formatIndices.Size() - 1]; | |
354 | |
355 HRESULT result = OpenArchive(codecs, arcTypeIndex, fileName, | |
356 archive0, formatIndex0, defaultItemName0, openArchiveCallback); | |
357 RINOK(result); | |
358 | |
359 if (formatIndices.Size() == 1) | |
360 return S_OK; | |
361 arcTypeIndex = -1; | |
362 if (formatIndices.Size() >= 2) | |
363 arcTypeIndex = formatIndices[formatIndices.Size() - 2]; | |
364 | |
365 HRESULT resSpec = (formatIndices.Size() == 0 ? S_OK : E_NOTIMPL); | |
366 | |
367 CMyComPtr<IInArchiveGetStream> getStream; | |
368 result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStr
eam); | |
369 if (result != S_OK || !getStream) | |
370 return resSpec; | |
371 | |
372 CMyComPtr<ISequentialInStream> subSeqStream; | |
373 result = getStream->GetStream(0, &subSeqStream); | |
374 if (result != S_OK || !subSeqStream) | |
375 return resSpec; | |
376 | |
377 CMyComPtr<IInStream> subStream; | |
378 result = subSeqStream.QueryInterface(IID_IInStream, &subStream); | |
379 if (result != S_OK || !subStream) | |
380 return resSpec; | |
381 | |
382 UInt32 numItems; | |
383 RINOK((*archive0)->GetNumberOfItems(&numItems)); | |
384 if (numItems < 1) | |
385 return resSpec; | |
386 | |
387 UString subPath; | |
388 RINOK(GetArchiveItemPath(*archive0, 0, subPath)) | |
389 if (subPath.IsEmpty()) | |
390 { | |
391 MakeDefaultName(defaultItemName0); | |
392 subPath = defaultItemName0; | |
393 const CArcInfoEx &format = codecs->Formats[formatIndex0]; | |
394 if (format.Name.CompareNoCase(L"7z") == 0) | |
395 { | |
396 if (subPath.Right(3).CompareNoCase(L".7z") != 0) | |
397 subPath += L".7z"; | |
398 } | |
399 } | |
400 else | |
401 subPath = ExtractFileNameFromPath(subPath); | |
402 | |
403 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; | |
404 openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void *
*)&setSubArchiveName); | |
405 if (setSubArchiveName) | |
406 setSubArchiveName->SetSubArchiveName(subPath); | |
407 | |
408 result = OpenArchive(codecs, arcTypeIndex, subStream, subPath, | |
409 archive1, formatIndex1, defaultItemName1, openArchiveCallback); | |
410 resSpec = (formatIndices.Size() == 0 ? S_OK : S_FALSE); | |
411 if (result != S_OK) | |
412 return resSpec; | |
413 return S_OK; | |
414 } | |
415 | |
416 static void SetCallback(const UString &archiveName, | |
417 IOpenCallbackUI *openCallbackUI, | |
418 IArchiveOpenCallback *reOpenCallback, | |
419 CMyComPtr<IArchiveOpenCallback> &openCallback) | |
420 { | |
421 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; | |
422 openCallback = openCallbackSpec; | |
423 openCallbackSpec->Callback = openCallbackUI; | |
424 openCallbackSpec->ReOpenCallback = reOpenCallback; | |
425 | |
426 UString fullName; | |
427 int fileNamePartStartIndex; | |
428 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartI
ndex); | |
429 openCallbackSpec->Init( | |
430 fullName.Left(fileNamePartStartIndex), | |
431 fullName.Mid(fileNamePartStartIndex)); | |
432 } | |
433 | |
434 HRESULT MyOpenArchive( | |
435 CCodecs *codecs, | |
436 int arcTypeIndex, | |
437 const UString &archiveName, | |
438 IInArchive **archive, UString &defaultItemName, IOpenCallbackUI *openCallbac
kUI) | |
439 { | |
440 CMyComPtr<IArchiveOpenCallback> openCallback; | |
441 SetCallback(archiveName, openCallbackUI, NULL, openCallback); | |
442 int formatInfo; | |
443 return OpenArchive(codecs, arcTypeIndex, archiveName, archive, formatInfo, def
aultItemName, openCallback); | |
444 } | |
445 | |
446 HRESULT MyOpenArchive( | |
447 CCodecs *codecs, | |
448 const CIntVector &formatIndices, | |
449 const UString &archiveName, | |
450 IInArchive **archive0, | |
451 IInArchive **archive1, | |
452 UString &defaultItemName0, | |
453 UString &defaultItemName1, | |
454 UStringVector &volumePaths, | |
455 UInt64 &volumesSize, | |
456 IOpenCallbackUI *openCallbackUI) | |
457 { | |
458 volumesSize = 0; | |
459 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; | |
460 CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec; | |
461 openCallbackSpec->Callback = openCallbackUI; | |
462 | |
463 UString fullName; | |
464 int fileNamePartStartIndex; | |
465 NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartI
ndex); | |
466 UString prefix = fullName.Left(fileNamePartStartIndex); | |
467 UString name = fullName.Mid(fileNamePartStartIndex); | |
468 openCallbackSpec->Init(prefix, name); | |
469 | |
470 int formatIndex0, formatIndex1; | |
471 RINOK(OpenArchive(codecs, formatIndices, archiveName, | |
472 archive0, | |
473 archive1, | |
474 formatIndex0, | |
475 formatIndex1, | |
476 defaultItemName0, | |
477 defaultItemName1, | |
478 openCallback)); | |
479 volumePaths.Add(prefix + name); | |
480 for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) | |
481 volumePaths.Add(prefix + openCallbackSpec->FileNames[i]); | |
482 volumesSize = openCallbackSpec->TotalSize; | |
483 return S_OK; | |
484 } | |
485 | |
486 HRESULT CArchiveLink::Close() | |
487 { | |
488 if (Archive1 != 0) | |
489 RINOK(Archive1->Close()); | |
490 if (Archive0 != 0) | |
491 RINOK(Archive0->Close()); | |
492 IsOpen = false; | |
493 return S_OK; | |
494 } | |
495 | |
496 void CArchiveLink::Release() | |
497 { | |
498 IsOpen = false; | |
499 Archive1.Release(); | |
500 Archive0.Release(); | |
501 } | |
502 | |
503 HRESULT OpenArchive( | |
504 CCodecs *codecs, | |
505 const CIntVector &formatIndices, | |
506 const UString &archiveName, | |
507 CArchiveLink &archiveLink, | |
508 IArchiveOpenCallback *openCallback) | |
509 { | |
510 HRESULT res = OpenArchive(codecs, formatIndices, archiveName, | |
511 &archiveLink.Archive0, &archiveLink.Archive1, | |
512 archiveLink.FormatIndex0, archiveLink.FormatIndex1, | |
513 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, | |
514 openCallback); | |
515 archiveLink.IsOpen = (res == S_OK); | |
516 return res; | |
517 } | |
518 | |
519 HRESULT MyOpenArchive(CCodecs *codecs, | |
520 const CIntVector &formatIndices, | |
521 const UString &archiveName, | |
522 CArchiveLink &archiveLink, | |
523 IOpenCallbackUI *openCallbackUI) | |
524 { | |
525 HRESULT res = MyOpenArchive(codecs, formatIndices, archiveName, | |
526 &archiveLink.Archive0, &archiveLink.Archive1, | |
527 archiveLink.DefaultItemName0, archiveLink.DefaultItemName1, | |
528 archiveLink.VolumePaths, | |
529 archiveLink.VolumesSize, | |
530 openCallbackUI); | |
531 archiveLink.IsOpen = (res == S_OK); | |
532 return res; | |
533 } | |
534 | |
535 HRESULT ReOpenArchive(CCodecs *codecs, CArchiveLink &archiveLink, const UString
&fileName, | |
536 IArchiveOpenCallback *openCallback) | |
537 { | |
538 if (archiveLink.GetNumLevels() > 1) | |
539 return E_NOTIMPL; | |
540 | |
541 if (archiveLink.GetNumLevels() == 0) | |
542 return MyOpenArchive(codecs, CIntVector(), fileName, archiveLink, 0); | |
543 | |
544 CMyComPtr<IArchiveOpenCallback> openCallbackNew; | |
545 SetCallback(fileName, NULL, openCallback, openCallbackNew); | |
546 | |
547 HRESULT res = ReOpenArchive(archiveLink.GetArchive(), fileName, openCallbackNe
w); | |
548 archiveLink.IsOpen = (res == S_OK); | |
549 return res; | |
550 } | |
OLD | NEW |