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