Home | History | Annotate | Download | only in 7z
      1 // 7zDecode.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../Common/LimitedStreams.h"
      6 #include "../../Common/ProgressUtils.h"
      7 #include "../../Common/StreamObjects.h"
      8 
      9 #include "7zDecode.h"
     10 
     11 namespace NArchive {
     12 namespace N7z {
     13 
     14 class CDecProgress:
     15   public ICompressProgressInfo,
     16   public CMyUnknownImp
     17 {
     18   CMyComPtr<ICompressProgressInfo> _progress;
     19 public:
     20   CDecProgress(ICompressProgressInfo *progress): _progress(progress) {}
     21 
     22   MY_UNKNOWN_IMP1(ICompressProgressInfo)
     23   STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
     24 };
     25 
     26 STDMETHODIMP CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize)
     27 {
     28   return _progress->SetRatioInfo(NULL, outSize);
     29 }
     30 
     31 static void Convert_FolderInfo_to_BindInfo(const CFolderEx &folder, CBindInfoEx &bi)
     32 {
     33   bi.Clear();
     34 
     35   bi.Bonds.ClearAndSetSize(folder.Bonds.Size());
     36   unsigned i;
     37   for (i = 0; i < folder.Bonds.Size(); i++)
     38   {
     39     NCoderMixer2::CBond &bond = bi.Bonds[i];
     40     const N7z::CBond &folderBond = folder.Bonds[i];
     41     bond.PackIndex = folderBond.PackIndex;
     42     bond.UnpackIndex = folderBond.UnpackIndex;
     43   }
     44 
     45   bi.Coders.ClearAndSetSize(folder.Coders.Size());
     46   bi.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size());
     47   for (i = 0; i < folder.Coders.Size(); i++)
     48   {
     49     const CCoderInfo &coderInfo = folder.Coders[i];
     50     bi.Coders[i].NumStreams = coderInfo.NumStreams;
     51     bi.CoderMethodIDs[i] = coderInfo.MethodID;
     52   }
     53 
     54   /*
     55   if (!bi.SetUnpackCoder())
     56     throw 1112;
     57   */
     58   bi.UnpackCoder = folder.UnpackCoder;
     59   bi.PackStreams.ClearAndSetSize(folder.PackStreams.Size());
     60   for (i = 0; i < folder.PackStreams.Size(); i++)
     61     bi.PackStreams[i] = folder.PackStreams[i];
     62 }
     63 
     64 static inline bool AreCodersEqual(
     65     const NCoderMixer2::CCoderStreamsInfo &a1,
     66     const NCoderMixer2::CCoderStreamsInfo &a2)
     67 {
     68   return (a1.NumStreams == a2.NumStreams);
     69 }
     70 
     71 static inline bool AreBondsEqual(
     72     const NCoderMixer2::CBond &a1,
     73     const NCoderMixer2::CBond &a2)
     74 {
     75   return
     76     (a1.PackIndex == a2.PackIndex) &&
     77     (a1.UnpackIndex == a2.UnpackIndex);
     78 }
     79 
     80 static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
     81 {
     82   if (a1.Coders.Size() != a2.Coders.Size())
     83     return false;
     84   unsigned i;
     85   for (i = 0; i < a1.Coders.Size(); i++)
     86     if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
     87       return false;
     88 
     89   if (a1.Bonds.Size() != a2.Bonds.Size())
     90     return false;
     91   for (i = 0; i < a1.Bonds.Size(); i++)
     92     if (!AreBondsEqual(a1.Bonds[i], a2.Bonds[i]))
     93       return false;
     94 
     95   for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
     96     if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
     97       return false;
     98 
     99   if (a1.PackStreams.Size() != a2.PackStreams.Size())
    100     return false;
    101   for (i = 0; i < a1.PackStreams.Size(); i++)
    102     if (a1.PackStreams[i] != a2.PackStreams[i])
    103       return false;
    104 
    105   /*
    106   if (a1.UnpackCoder != a2.UnpackCoder)
    107     return false;
    108   */
    109   return true;
    110 }
    111 
    112 CDecoder::CDecoder(bool useMixerMT):
    113     _bindInfoPrev_Defined(false),
    114     _useMixerMT(useMixerMT)
    115 {}
    116 
    117 
    118 struct CLockedInStream:
    119   public IUnknown,
    120   public CMyUnknownImp
    121 {
    122   CMyComPtr<IInStream> Stream;
    123   UInt64 Pos;
    124 
    125   MY_UNKNOWN_IMP
    126 
    127   #ifdef USE_MIXER_MT
    128   NWindows::NSynchronization::CCriticalSection CriticalSection;
    129   #endif
    130 };
    131 
    132 
    133 #ifdef USE_MIXER_MT
    134 
    135 class CLockedSequentialInStreamMT:
    136   public ISequentialInStream,
    137   public CMyUnknownImp
    138 {
    139   CLockedInStream *_glob;
    140   UInt64 _pos;
    141   CMyComPtr<IUnknown> _globRef;
    142 public:
    143   void Init(CLockedInStream *lockedInStream, UInt64 startPos)
    144   {
    145     _globRef = lockedInStream;
    146     _glob = lockedInStream;
    147     _pos = startPos;
    148   }
    149 
    150   MY_UNKNOWN_IMP1(ISequentialInStream)
    151 
    152   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
    153 };
    154 
    155 STDMETHODIMP CLockedSequentialInStreamMT::Read(void *data, UInt32 size, UInt32 *processedSize)
    156 {
    157   NWindows::NSynchronization::CCriticalSectionLock lock(_glob->CriticalSection);
    158 
    159   if (_pos != _glob->Pos)
    160   {
    161     RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL));
    162     _glob->Pos = _pos;
    163   }
    164 
    165   UInt32 realProcessedSize = 0;
    166   HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
    167   _pos += realProcessedSize;
    168   _glob->Pos = _pos;
    169   if (processedSize)
    170     *processedSize = realProcessedSize;
    171   return res;
    172 }
    173 
    174 #endif
    175 
    176 
    177 #ifdef USE_MIXER_ST
    178 
    179 class CLockedSequentialInStreamST:
    180   public ISequentialInStream,
    181   public CMyUnknownImp
    182 {
    183   CLockedInStream *_glob;
    184   UInt64 _pos;
    185   CMyComPtr<IUnknown> _globRef;
    186 public:
    187   void Init(CLockedInStream *lockedInStream, UInt64 startPos)
    188   {
    189     _globRef = lockedInStream;
    190     _glob = lockedInStream;
    191     _pos = startPos;
    192   }
    193 
    194   MY_UNKNOWN_IMP1(ISequentialInStream)
    195 
    196   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
    197 };
    198 
    199 STDMETHODIMP CLockedSequentialInStreamST::Read(void *data, UInt32 size, UInt32 *processedSize)
    200 {
    201   if (_pos != _glob->Pos)
    202   {
    203     RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL));
    204     _glob->Pos = _pos;
    205   }
    206 
    207   UInt32 realProcessedSize = 0;
    208   HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
    209   _pos += realProcessedSize;
    210   _glob->Pos = _pos;
    211   if (processedSize)
    212     *processedSize = realProcessedSize;
    213   return res;
    214 }
    215 
    216 #endif
    217 
    218 
    219 
    220 HRESULT CDecoder::Decode(
    221     DECL_EXTERNAL_CODECS_LOC_VARS
    222     IInStream *inStream,
    223     UInt64 startPos,
    224     const CFolders &folders, unsigned folderIndex,
    225     const UInt64 *unpackSize
    226 
    227     , ISequentialOutStream *outStream
    228     , ICompressProgressInfo *compressProgress
    229     , ISequentialInStream **
    230 
    231     #ifdef USE_MIXER_ST
    232     inStreamMainRes
    233     #endif
    234 
    235     _7Z_DECODER_CRYPRO_VARS_DECL
    236 
    237     #if !defined(_7ZIP_ST) && !defined(_SFX)
    238     , bool mtMode, UInt32 numThreads
    239     #endif
    240     )
    241 {
    242   const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]];
    243   CFolderEx folderInfo;
    244   folders.ParseFolderEx(folderIndex, folderInfo);
    245 
    246   if (!folderInfo.IsDecodingSupported())
    247     return E_NOTIMPL;
    248 
    249   CBindInfoEx bindInfo;
    250   Convert_FolderInfo_to_BindInfo(folderInfo, bindInfo);
    251   if (!bindInfo.CalcMapsAndCheck())
    252     return E_NOTIMPL;
    253 
    254   UInt64 folderUnpackSize = folders.GetFolderUnpackSize(folderIndex);
    255   bool fullUnpack = true;
    256   if (unpackSize)
    257   {
    258     if (*unpackSize > folderUnpackSize)
    259       return E_FAIL;
    260     fullUnpack = (*unpackSize == folderUnpackSize);
    261   }
    262 
    263   /*
    264   We don't need to init isEncrypted and passwordIsDefined
    265   We must upgrade them only
    266 
    267   #ifndef _NO_CRYPTO
    268   isEncrypted = false;
    269   passwordIsDefined = false;
    270   #endif
    271   */
    272 
    273   if (!_bindInfoPrev_Defined || !AreBindInfoExEqual(bindInfo, _bindInfoPrev))
    274   {
    275     _mixerRef.Release();
    276 
    277     #ifdef USE_MIXER_MT
    278     #ifdef USE_MIXER_ST
    279     if (_useMixerMT)
    280     #endif
    281     {
    282       _mixerMT = new NCoderMixer2::CMixerMT(false);
    283       _mixerRef = _mixerMT;
    284       _mixer = _mixerMT;
    285     }
    286     #ifdef USE_MIXER_ST
    287     else
    288     #endif
    289     #endif
    290     {
    291       #ifdef USE_MIXER_ST
    292       _mixerST = new NCoderMixer2::CMixerST(false);
    293       _mixerRef = _mixerST;
    294       _mixer = _mixerST;
    295       #endif
    296     }
    297 
    298     RINOK(_mixer->SetBindInfo(bindInfo));
    299 
    300     FOR_VECTOR(i, folderInfo.Coders)
    301     {
    302       const CCoderInfo &coderInfo = folderInfo.Coders[i];
    303 
    304       #ifndef _SFX
    305       // we don't support RAR codecs here
    306       if ((coderInfo.MethodID >> 8) == 0x403)
    307         return E_NOTIMPL;
    308       #endif
    309 
    310       CCreatedCoder cod;
    311       RINOK(CreateCoder(
    312           EXTERNAL_CODECS_LOC_VARS
    313           coderInfo.MethodID, false, cod));
    314 
    315       if (coderInfo.IsSimpleCoder())
    316       {
    317         if (!cod.Coder)
    318           return E_NOTIMPL;
    319         // CMethodId m = coderInfo.MethodID;
    320         // isFilter = (IsFilterMethod(m) || m == k_AES);
    321       }
    322       else
    323       {
    324         if (!cod.Coder2 || cod.NumStreams != coderInfo.NumStreams)
    325           return E_NOTIMPL;
    326       }
    327       _mixer->AddCoder(cod);
    328 
    329       // now there is no codec that uses another external codec
    330       /*
    331       #ifdef EXTERNAL_CODECS
    332       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
    333       decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
    334       if (setCompressCodecsInfo)
    335       {
    336         // we must use g_ExternalCodecs also
    337         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs));
    338       }
    339       #endif
    340       */
    341     }
    342 
    343     _bindInfoPrev = bindInfo;
    344     _bindInfoPrev_Defined = true;
    345   }
    346 
    347   _mixer->ReInit();
    348 
    349   UInt32 packStreamIndex = 0;
    350   UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex];
    351 
    352   unsigned i;
    353 
    354   for (i = 0; i < folderInfo.Coders.Size(); i++)
    355   {
    356     const CCoderInfo &coderInfo = folderInfo.Coders[i];
    357     IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();
    358 
    359     {
    360       CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
    361       decoder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
    362       if (setDecoderProperties)
    363       {
    364         const CByteBuffer &props = coderInfo.Props;
    365         size_t size = props.Size();
    366         if (size > 0xFFFFFFFF)
    367           return E_NOTIMPL;
    368         HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size);
    369         if (res == E_INVALIDARG)
    370           res = E_NOTIMPL;
    371         RINOK(res);
    372       }
    373     }
    374 
    375     #if !defined(_7ZIP_ST) && !defined(_SFX)
    376     if (mtMode)
    377     {
    378       CMyComPtr<ICompressSetCoderMt> setCoderMt;
    379       decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
    380       if (setCoderMt)
    381       {
    382         RINOK(setCoderMt->SetNumberOfThreads(numThreads));
    383       }
    384     }
    385     #endif
    386 
    387     #ifndef _NO_CRYPTO
    388     {
    389       CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
    390       decoder->QueryInterface(IID_ICryptoSetPassword, (void **)&cryptoSetPassword);
    391       if (cryptoSetPassword)
    392       {
    393         isEncrypted = true;
    394         if (!getTextPassword)
    395           return E_NOTIMPL;
    396         CMyComBSTR passwordBSTR;
    397         RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
    398         passwordIsDefined = true;
    399         password.Empty();
    400         size_t len = 0;
    401         if (passwordBSTR)
    402         {
    403           password = passwordBSTR;
    404           len = password.Len();
    405         }
    406         CByteBuffer buffer(len * 2);
    407         for (size_t k = 0; k < len; k++)
    408         {
    409           wchar_t c = passwordBSTR[k];
    410           ((Byte *)buffer)[k * 2] = (Byte)c;
    411           ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
    412         }
    413         RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()));
    414       }
    415     }
    416     #endif
    417 
    418     {
    419       CMyComPtr<ICompressSetFinishMode> setFinishMode;
    420       decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
    421       if (setFinishMode)
    422       {
    423         RINOK(setFinishMode->SetFinishMode(BoolToInt(fullUnpack)));
    424       }
    425     }
    426 
    427     UInt32 numStreams = (UInt32)coderInfo.NumStreams;
    428 
    429     CObjArray<UInt64> packSizes(numStreams);
    430     CObjArray<const UInt64 *> packSizesPointers(numStreams);
    431 
    432     for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
    433     {
    434       int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
    435 
    436       if (bond >= 0)
    437         packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
    438       else
    439       {
    440         int index = folderInfo.Find_in_PackStreams(packStreamIndex);
    441         if (index < 0)
    442           return E_NOTIMPL;
    443         packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
    444         packSizesPointers[j] = &packSizes[j];
    445       }
    446     }
    447 
    448     const UInt64 *unpackSizesPointer =
    449         (unpackSize && i == bindInfo.UnpackCoder) ?
    450             unpackSize :
    451             &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
    452 
    453     _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers);
    454   }
    455 
    456   if (outStream)
    457   {
    458     _mixer->SelectMainCoder(!fullUnpack);
    459   }
    460 
    461   CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
    462 
    463   CLockedInStream *lockedInStreamSpec = new CLockedInStream;
    464   CMyComPtr<IUnknown> lockedInStream = lockedInStreamSpec;
    465 
    466   bool needMtLock = false;
    467 
    468   if (folderInfo.PackStreams.Size() > 1)
    469   {
    470     // lockedInStream.Pos = (UInt64)(Int64)-1;
    471     // RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &lockedInStream.Pos));
    472     RINOK(inStream->Seek(startPos + packPositions[0], STREAM_SEEK_SET, &lockedInStreamSpec->Pos));
    473     lockedInStreamSpec->Stream = inStream;
    474 
    475     #ifdef USE_MIXER_ST
    476     if (_mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex))
    477     #endif
    478       needMtLock = true;
    479   }
    480 
    481   for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++)
    482   {
    483     CMyComPtr<ISequentialInStream> packStream;
    484     UInt64 packPos = startPos + packPositions[j];
    485 
    486     if (folderInfo.PackStreams.Size() == 1)
    487     {
    488       RINOK(inStream->Seek(packPos, STREAM_SEEK_SET, NULL));
    489       packStream = inStream;
    490     }
    491     else
    492     {
    493       #ifdef USE_MIXER_MT
    494       #ifdef USE_MIXER_ST
    495       if (_useMixerMT || needMtLock)
    496       #endif
    497       {
    498         CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT;
    499         packStream = lockedStreamImpSpec;
    500         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
    501       }
    502       #ifdef USE_MIXER_ST
    503       else
    504       #endif
    505       #endif
    506       {
    507         #ifdef USE_MIXER_ST
    508         CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST;
    509         packStream = lockedStreamImpSpec;
    510         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
    511         #endif
    512       }
    513     }
    514 
    515     CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
    516     inStreams.AddNew() = streamSpec;
    517     streamSpec->SetStream(packStream);
    518     streamSpec->Init(packPositions[j + 1] - packPositions[j]);
    519   }
    520 
    521   unsigned num = inStreams.Size();
    522   CObjArray<ISequentialInStream *> inStreamPointers(num);
    523   for (i = 0; i < num; i++)
    524     inStreamPointers[i] = inStreams[i];
    525 
    526   if (outStream)
    527   {
    528     CMyComPtr<ICompressProgressInfo> progress2;
    529     if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex))
    530       progress2 = new CDecProgress(compressProgress);
    531 
    532     ISequentialOutStream *outStreamPointer = outStream;
    533     return _mixer->Code(inStreamPointers, &outStreamPointer, progress2 ? (ICompressProgressInfo *)progress2 : compressProgress);
    534   }
    535 
    536   #ifdef USE_MIXER_ST
    537     return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);
    538   #else
    539     return E_FAIL;
    540   #endif
    541 }
    542 
    543 }}
    544