Home | History | Annotate | Download | only in Archive
      1 // SplitHandler.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../Common/ComTry.h"
      6 #include "../../Common/MyString.h"
      7 
      8 #include "../../Windows/PropVariant.h"
      9 
     10 #include "../Common/ProgressUtils.h"
     11 #include "../Common/RegisterArc.h"
     12 
     13 #include "../Compress/CopyCoder.h"
     14 
     15 #include "Common/MultiStream.h"
     16 
     17 using namespace NWindows;
     18 
     19 namespace NArchive {
     20 namespace NSplit {
     21 
     22 static const Byte kProps[] =
     23 {
     24   kpidPath,
     25   kpidSize
     26 };
     27 
     28 static const Byte kArcProps[] =
     29 {
     30   kpidNumVolumes,
     31   kpidTotalPhySize
     32 };
     33 
     34 class CHandler:
     35   public IInArchive,
     36   public IInArchiveGetStream,
     37   public CMyUnknownImp
     38 {
     39   CObjectVector<CMyComPtr<IInStream> > _streams;
     40   CRecordVector<UInt64> _sizes;
     41   UString _subName;
     42   UInt64 _totalSize;
     43 
     44   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
     45 public:
     46   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
     47   INTERFACE_IInArchive(;)
     48   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
     49 };
     50 
     51 IMP_IInArchive_Props
     52 IMP_IInArchive_ArcProps
     53 
     54 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
     55 {
     56   NCOM::CPropVariant prop;
     57   switch (propID)
     58   {
     59     case kpidMainSubfile: prop = (UInt32)0; break;
     60     case kpidPhySize: if (!_sizes.IsEmpty()) prop = _sizes[0]; break;
     61     case kpidTotalPhySize: prop = _totalSize; break;
     62     case kpidNumVolumes: prop = (UInt32)_streams.Size(); break;
     63   }
     64   prop.Detach(value);
     65   return S_OK;
     66 }
     67 
     68 struct CSeqName
     69 {
     70   UString _unchangedPart;
     71   UString _changedPart;
     72   bool _splitStyle;
     73 
     74   bool GetNextName(UString &s)
     75   {
     76     {
     77       unsigned i = _changedPart.Len();
     78       for (;;)
     79       {
     80         wchar_t c = _changedPart[--i];
     81 
     82         if (_splitStyle)
     83         {
     84           if (c == 'z')
     85           {
     86             _changedPart.ReplaceOneCharAtPos(i, L'a');
     87             if (i == 0)
     88               return false;
     89             continue;
     90           }
     91           else if (c == 'Z')
     92           {
     93             _changedPart.ReplaceOneCharAtPos(i, L'A');
     94             if (i == 0)
     95               return false;
     96             continue;
     97           }
     98         }
     99         else
    100         {
    101           if (c == '9')
    102           {
    103             _changedPart.ReplaceOneCharAtPos(i, L'0');
    104             if (i == 0)
    105             {
    106               _changedPart.InsertAtFront(L'1');
    107               break;
    108             }
    109             continue;
    110           }
    111         }
    112 
    113         c++;
    114         _changedPart.ReplaceOneCharAtPos(i, c);
    115         break;
    116       }
    117     }
    118 
    119     s = _unchangedPart + _changedPart;
    120     return true;
    121   }
    122 };
    123 
    124 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
    125 {
    126   Close();
    127   if (!callback)
    128     return S_FALSE;
    129 
    130   CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback;
    131   callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback);
    132   if (!volumeCallback)
    133     return S_FALSE;
    134 
    135   UString name;
    136   {
    137     NCOM::CPropVariant prop;
    138     RINOK(volumeCallback->GetProperty(kpidName, &prop));
    139     if (prop.vt != VT_BSTR)
    140       return S_FALSE;
    141     name = prop.bstrVal;
    142   }
    143 
    144   int dotPos = name.ReverseFind_Dot();
    145   const UString prefix = name.Left(dotPos + 1);
    146   const UString ext = name.Ptr(dotPos + 1);
    147   UString ext2 = ext;
    148   ext2.MakeLower_Ascii();
    149 
    150   CSeqName seqName;
    151 
    152   unsigned numLetters = 2;
    153   bool splitStyle = false;
    154 
    155   if (ext2.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "aa"))
    156   {
    157     splitStyle = true;
    158     while (numLetters < ext2.Len())
    159     {
    160       if (ext2[ext2.Len() - numLetters - 1] != 'a')
    161         break;
    162       numLetters++;
    163     }
    164   }
    165   else if (ext.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "01"))
    166   {
    167     while (numLetters < ext2.Len())
    168     {
    169       if (ext2[ext2.Len() - numLetters - 1] != '0')
    170         break;
    171       numLetters++;
    172     }
    173     if (numLetters != ext.Len())
    174       return S_FALSE;
    175   }
    176   else
    177     return S_FALSE;
    178 
    179   seqName._unchangedPart = prefix + ext.Left(ext2.Len() - numLetters);
    180   seqName._changedPart = ext.RightPtr(numLetters);
    181   seqName._splitStyle = splitStyle;
    182 
    183   if (prefix.Len() < 1)
    184     _subName.SetFromAscii("file");
    185   else
    186     _subName.SetFrom(prefix, prefix.Len() - 1);
    187 
    188   UInt64 size;
    189   {
    190     /*
    191     NCOM::CPropVariant prop;
    192     RINOK(volumeCallback->GetProperty(kpidSize, &prop));
    193     if (prop.vt != VT_UI8)
    194       return E_INVALIDARG;
    195     size = prop.uhVal.QuadPart;
    196     */
    197     RINOK(stream->Seek(0, STREAM_SEEK_END, &size));
    198     RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
    199   }
    200 
    201   _totalSize += size;
    202   _sizes.Add(size);
    203   _streams.Add(stream);
    204 
    205   {
    206     const UInt64 numFiles = _streams.Size();
    207     RINOK(callback->SetCompleted(&numFiles, NULL));
    208   }
    209 
    210   for (;;)
    211   {
    212     UString fullName;
    213     if (!seqName.GetNextName(fullName))
    214       break;
    215     CMyComPtr<IInStream> nextStream;
    216     HRESULT result = volumeCallback->GetStream(fullName, &nextStream);
    217     if (result == S_FALSE)
    218       break;
    219     if (result != S_OK)
    220       return result;
    221     if (!nextStream)
    222       break;
    223     {
    224       /*
    225       NCOM::CPropVariant prop;
    226       RINOK(volumeCallback->GetProperty(kpidSize, &prop));
    227       if (prop.vt != VT_UI8)
    228         return E_INVALIDARG;
    229       size = prop.uhVal.QuadPart;
    230       */
    231       RINOK(nextStream->Seek(0, STREAM_SEEK_END, &size));
    232       RINOK(nextStream->Seek(0, STREAM_SEEK_SET, NULL));
    233     }
    234     _totalSize += size;
    235     _sizes.Add(size);
    236     _streams.Add(nextStream);
    237     {
    238       const UInt64 numFiles = _streams.Size();
    239       RINOK(callback->SetCompleted(&numFiles, NULL));
    240     }
    241   }
    242 
    243   if (_streams.Size() == 1)
    244   {
    245     if (splitStyle)
    246       return S_FALSE;
    247   }
    248   return S_OK;
    249 }
    250 
    251 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
    252 {
    253   COM_TRY_BEGIN
    254   HRESULT res = Open2(stream, callback);
    255   if (res != S_OK)
    256     Close();
    257   return res;
    258   COM_TRY_END
    259 }
    260 
    261 STDMETHODIMP CHandler::Close()
    262 {
    263   _totalSize = 0;
    264   _subName.Empty();
    265   _streams.Clear();
    266   _sizes.Clear();
    267   return S_OK;
    268 }
    269 
    270 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
    271 {
    272   *numItems = _streams.IsEmpty() ? 0 : 1;
    273   return S_OK;
    274 }
    275 
    276 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
    277 {
    278   NCOM::CPropVariant prop;
    279   switch (propID)
    280   {
    281     case kpidPath: prop = _subName; break;
    282     case kpidSize:
    283     case kpidPackSize:
    284       prop = _totalSize;
    285       break;
    286   }
    287   prop.Detach(value);
    288   return S_OK;
    289 }
    290 
    291 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    292     Int32 testMode, IArchiveExtractCallback *extractCallback)
    293 {
    294   COM_TRY_BEGIN
    295   if (numItems == 0)
    296     return S_OK;
    297   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
    298     return E_INVALIDARG;
    299 
    300   UInt64 currentTotalSize = 0;
    301   RINOK(extractCallback->SetTotal(_totalSize));
    302   CMyComPtr<ISequentialOutStream> outStream;
    303   Int32 askMode = testMode ?
    304       NExtract::NAskMode::kTest :
    305       NExtract::NAskMode::kExtract;
    306   RINOK(extractCallback->GetStream(0, &outStream, askMode));
    307   if (!testMode && !outStream)
    308     return S_OK;
    309   RINOK(extractCallback->PrepareOperation(askMode));
    310 
    311   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
    312   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
    313 
    314   CLocalProgress *lps = new CLocalProgress;
    315   CMyComPtr<ICompressProgressInfo> progress = lps;
    316   lps->Init(extractCallback, false);
    317 
    318   FOR_VECTOR (i, _streams)
    319   {
    320     lps->InSize = lps->OutSize = currentTotalSize;
    321     RINOK(lps->SetCur());
    322     IInStream *inStream = _streams[i];
    323     RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
    324     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
    325     currentTotalSize += copyCoderSpec->TotalSize;
    326   }
    327   outStream.Release();
    328   return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK);
    329   COM_TRY_END
    330 }
    331 
    332 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
    333 {
    334   COM_TRY_BEGIN
    335   if (index != 0)
    336     return E_INVALIDARG;
    337   *stream = 0;
    338   CMultiStream *streamSpec = new CMultiStream;
    339   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
    340   FOR_VECTOR (i, _streams)
    341   {
    342     CMultiStream::CSubStreamInfo subStreamInfo;
    343     subStreamInfo.Stream = _streams[i];
    344     subStreamInfo.Size = _sizes[i];
    345     streamSpec->Streams.Add(subStreamInfo);
    346   }
    347   streamSpec->Init();
    348   *stream = streamTemp.Detach();
    349   return S_OK;
    350   COM_TRY_END
    351 }
    352 
    353 REGISTER_ARC_I_NO_SIG(
    354   "Split", "001", 0, 0xEA,
    355   0,
    356   0,
    357   NULL)
    358 
    359 }}
    360