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