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