Home | History | Annotate | Download | only in Common
      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 
     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 
     33 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
     34 {
     35   return GetArchiveItemBoolProp(archive, index, kpidIsDir, result);
     36 }
     37 
     38 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 
     66 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
     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 
    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 
    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 
    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 
    369 void CArchiveLink::Release()
    370 {
    371   while (!Arcs.IsEmpty())
    372     Arcs.DeleteBack();
    373 }
    374 
    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 
    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 
    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 
    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