1 // ComHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "Common/ComTry.h"
6 
7 #include "Windows/PropVariant.h"
8 
9 #include "../../Common/LimitedStreams.h"
10 #include "../../Common/ProgressUtils.h"
11 #include "../../Common/StreamUtils.h"
12 
13 #include "../../Compress/CopyCoder.h"
14 
15 #include "ComHandler.h"
16 
17 namespace NArchive {
18 namespace NCom {
19 
20 STATPROPSTG kProps[] =
21 {
22   { NULL, kpidPath, VT_BSTR},
23   { NULL, kpidIsDir, VT_BOOL},
24   { NULL, kpidSize, VT_UI8},
25   { NULL, kpidPackSize, VT_UI8},
26   { NULL, kpidCTime, VT_FILETIME},
27   { NULL, kpidMTime, VT_FILETIME}
28 };
29 
30 STATPROPSTG kArcProps[] =
31 {
32   { NULL, kpidClusterSize, VT_UI4},
33   { NULL, kpidSectorSize, VT_UI4}
34 };
35 
36 IMP_IInArchive_Props
37 IMP_IInArchive_ArcProps
38 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)39 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
40 {
41   COM_TRY_BEGIN
42   NWindows::NCOM::CPropVariant prop;
43   switch(propID)
44   {
45     case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break;
46     case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break;
47   }
48   prop.Detach(value);
49   return S_OK;
50   COM_TRY_END
51 }
52 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)53 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
54 {
55   COM_TRY_BEGIN
56   NWindows::NCOM::CPropVariant prop;
57   const CRef &ref = _db.Refs[index];
58   const CItem &item = _db.Items[ref.Did];
59 
60   switch(propID)
61   {
62     case kpidPath:  prop = _db.GetItemPath(index); break;
63     case kpidIsDir:  prop = item.IsDir(); break;
64     case kpidCTime:  prop = item.CTime; break;
65     case kpidMTime:  prop = item.MTime; break;
66     case kpidPackSize:  if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break;
67     case kpidSize:  if (!item.IsDir()) prop = item.Size; break;
68   }
69   prop.Detach(value);
70   return S_OK;
71   COM_TRY_END
72 }
73 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)74 STDMETHODIMP CHandler::Open(IInStream *inStream,
75     const UInt64 * /* maxCheckStartPosition */,
76     IArchiveOpenCallback * /* openArchiveCallback */)
77 {
78   COM_TRY_BEGIN
79   Close();
80   try
81   {
82     if (_db.Open(inStream) != S_OK)
83       return S_FALSE;
84     _stream = inStream;
85   }
86   catch(...) { return S_FALSE; }
87   return S_OK;
88   COM_TRY_END
89 }
90 
Close()91 STDMETHODIMP CHandler::Close()
92 {
93   _db.Clear();
94   _stream.Release();
95   return S_OK;
96 }
97 
Extract(const UInt32 * indices,UInt32 numItems,Int32 _aTestMode,IArchiveExtractCallback * extractCallback)98 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
99     Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
100 {
101   COM_TRY_BEGIN
102   bool testMode = (_aTestMode != 0);
103   bool allFilesMode = (numItems == UInt32(-1));
104   if (allFilesMode)
105     numItems = _db.Refs.Size();
106   if (numItems == 0)
107     return S_OK;
108   UInt32 i;
109   UInt64 totalSize = 0;
110   for(i = 0; i < numItems; i++)
111   {
112     const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did];
113     if (!item.IsDir())
114       totalSize += item.Size;
115   }
116   RINOK(extractCallback->SetTotal(totalSize));
117 
118   UInt64 totalPackSize;
119   totalSize = totalPackSize = 0;
120 
121   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
122   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
123 
124   CLocalProgress *lps = new CLocalProgress;
125   CMyComPtr<ICompressProgressInfo> progress = lps;
126   lps->Init(extractCallback, false);
127 
128   for (i = 0; i < numItems; i++)
129   {
130     lps->InSize = totalPackSize;
131     lps->OutSize = totalSize;
132     RINOK(lps->SetCur());
133     Int32 index = allFilesMode ? i : indices[i];
134     const CItem &item = _db.Items[_db.Refs[index].Did];
135 
136     CMyComPtr<ISequentialOutStream> outStream;
137     Int32 askMode = testMode ?
138         NArchive::NExtract::NAskMode::kTest :
139         NArchive::NExtract::NAskMode::kExtract;
140     RINOK(extractCallback->GetStream(index, &outStream, askMode));
141 
142     if (item.IsDir())
143     {
144       RINOK(extractCallback->PrepareOperation(askMode));
145       RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
146       continue;
147     }
148 
149     totalPackSize += _db.GetItemPackSize(item.Size);
150     totalSize += item.Size;
151 
152     if (!testMode && (!outStream))
153       continue;
154     RINOK(extractCallback->PrepareOperation(askMode));
155     Int32 res = NArchive::NExtract::NOperationResult::kDataError;
156     CMyComPtr<ISequentialInStream> inStream;
157     HRESULT hres = GetStream(index, &inStream);
158     if (hres == S_FALSE)
159       res = NArchive::NExtract::NOperationResult::kDataError;
160     else if (hres == E_NOTIMPL)
161       res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
162     else
163     {
164       RINOK(hres);
165       if (inStream)
166       {
167         RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
168         if (copyCoderSpec->TotalSize == item.Size)
169           res = NArchive::NExtract::NOperationResult::kOK;
170       }
171     }
172     outStream.Release();
173     RINOK(extractCallback->SetOperationResult(res));
174   }
175   return S_OK;
176   COM_TRY_END
177 }
178 
GetNumberOfItems(UInt32 * numItems)179 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
180 {
181   *numItems = _db.Refs.Size();
182   return S_OK;
183 }
184 
GetStream(UInt32 index,ISequentialInStream ** stream)185 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
186 {
187   COM_TRY_BEGIN
188   *stream = 0;
189   const CItem &item = _db.Items[_db.Refs[index].Did];
190   CClusterInStream *streamSpec = new CClusterInStream;
191   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
192   streamSpec->Stream = _stream;
193   streamSpec->StartOffset = 0;
194 
195   bool isLargeStream = _db.IsLargeStream(item.Size);
196   int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits;
197   streamSpec->BlockSizeLog = bsLog;
198   streamSpec->Size = item.Size;
199 
200   UInt32 clusterSize = (UInt32)1 << bsLog;
201   UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog;
202   if (numClusters64 >= ((UInt32)1 << 31))
203     return E_NOTIMPL;
204   streamSpec->Vector.Reserve((int)numClusters64);
205   UInt32 sid = item.Sid;
206   UInt64 size = item.Size;
207 
208   if (size != 0)
209   {
210     for (;; size -= clusterSize)
211     {
212       if (isLargeStream)
213       {
214         if (sid >= _db.FatSize)
215           return S_FALSE;
216         streamSpec->Vector.Add(sid + 1);
217         sid = _db.Fat[sid];
218       }
219       else
220       {
221         UInt64 val;
222         if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32)
223           return S_FALSE;
224         streamSpec->Vector.Add((UInt32)val);
225         sid = _db.Mat[sid];
226       }
227       if (size <= clusterSize)
228         break;
229     }
230   }
231   if (sid != NFatID::kEndOfChain)
232     return S_FALSE;
233   RINOK(streamSpec->InitAndSeek());
234   *stream = streamTemp.Detach();
235   return S_OK;
236   COM_TRY_END
237 }
238 
239 }}
240