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