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