1 // HfsHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "Common/ComTry.h"
6 #include "Windows/PropVariant.h"
7 #include "../../Common/StreamUtils.h"
8 #include "HfsHandler.h"
9
10 namespace NArchive {
11 namespace NHfs {
12
13 STATPROPSTG kProps[] =
14 {
15 { NULL, kpidPath, VT_BSTR},
16 { NULL, kpidIsDir, VT_BOOL},
17 { NULL, kpidSize, VT_UI8},
18 { NULL, kpidPackSize, VT_UI8},
19 { NULL, kpidCTime, VT_FILETIME},
20 { NULL, kpidMTime, VT_FILETIME},
21 { NULL, kpidATime, VT_FILETIME}
22 };
23
24 STATPROPSTG kArcProps[] =
25 {
26 { NULL, kpidMethod, VT_BSTR},
27 { NULL, kpidClusterSize, VT_UI4},
28 { NULL, kpidFreeSpace, VT_UI8},
29 { NULL, kpidCTime, VT_FILETIME},
30 { NULL, kpidMTime, VT_FILETIME}
31 };
32
33 IMP_IInArchive_Props
34 IMP_IInArchive_ArcProps
35
HfsTimeToProp(UInt32 hfsTime,NWindows::NCOM::CPropVariant & prop)36 static void HfsTimeToProp(UInt32 hfsTime, NWindows::NCOM::CPropVariant &prop)
37 {
38 FILETIME ft;
39 HfsTimeToFileTime(hfsTime, ft);
40 prop = ft;
41 }
42
GetArchiveProperty(PROPID propID,PROPVARIANT * value)43 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
44 {
45 COM_TRY_BEGIN
46 NWindows::NCOM::CPropVariant prop;
47 switch(propID)
48 {
49 case kpidMethod: prop = _db.Header.IsHfsX() ? L"HFSX" : L"HFS+"; break;
50 case kpidClusterSize: prop = (UInt32)1 << _db.Header.BlockSizeLog; break;
51 case kpidFreeSpace: prop = (UInt64)_db.Header.NumFreeBlocks << _db.Header.BlockSizeLog; break;
52 case kpidMTime: HfsTimeToProp(_db.Header.MTime, prop); break;
53 case kpidCTime:
54 {
55 FILETIME localFt, ft;
56 HfsTimeToFileTime(_db.Header.CTime, localFt);
57 if (LocalFileTimeToFileTime(&localFt, &ft))
58 prop = ft;
59 break;
60 }
61 }
62 prop.Detach(value);
63 return S_OK;
64 COM_TRY_END
65 }
66
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)67 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
68 {
69 COM_TRY_BEGIN
70 NWindows::NCOM::CPropVariant prop;
71 const CItem &item = _db.Items[index];
72 switch(propID)
73 {
74 case kpidPath: prop = _db.GetItemPath(index); break;
75 case kpidIsDir: prop = item.IsDir(); break;
76
77 case kpidCTime: HfsTimeToProp(item.CTime, prop); break;
78 case kpidMTime: HfsTimeToProp(item.MTime, prop); break;
79 case kpidATime: HfsTimeToProp(item.ATime, prop); break;
80
81 case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumBlocks << _db.Header.BlockSizeLog; break;
82 case kpidSize: if (!item.IsDir()) prop = item.Size; break;
83 }
84 prop.Detach(value);
85 return S_OK;
86 COM_TRY_END
87 }
88
89 class CProgressImp: public CProgressVirt
90 {
91 CMyComPtr<IArchiveOpenCallback> _callback;
92 public:
93 HRESULT SetTotal(UInt64 numFiles);
94 HRESULT SetCompleted(UInt64 numFiles);
CProgressImp(IArchiveOpenCallback * callback)95 CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}
96 };
97
SetTotal(UInt64 numFiles)98 HRESULT CProgressImp::SetTotal(UInt64 numFiles)
99 {
100 if (_callback)
101 return _callback->SetTotal(&numFiles, NULL);
102 return S_OK;
103 }
104
SetCompleted(UInt64 numFiles)105 HRESULT CProgressImp::SetCompleted(UInt64 numFiles)
106 {
107 if (_callback)
108 return _callback->SetCompleted(&numFiles, NULL);
109 return S_OK;
110 }
111
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)112 STDMETHODIMP CHandler::Open(IInStream *inStream,
113 const UInt64 * /* maxCheckStartPosition */,
114 IArchiveOpenCallback *callback)
115 {
116 COM_TRY_BEGIN
117 Close();
118 try
119 {
120 CProgressImp progressImp(callback);
121 HRESULT res = _db.Open(inStream, &progressImp);
122 if (res == E_ABORT)
123 return res;
124 if (res != S_OK)
125 return S_FALSE;
126 _stream = inStream;
127 }
128 catch(...) { return S_FALSE; }
129 return S_OK;
130 COM_TRY_END
131 }
132
Close()133 STDMETHODIMP CHandler::Close()
134 {
135 _stream.Release();
136 _db.Clear();
137 return S_OK;
138 }
139
Extract(const UInt32 * indices,UInt32 numItems,Int32 _aTestMode,IArchiveExtractCallback * extractCallback)140 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
141 Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
142 {
143 COM_TRY_BEGIN
144 bool testMode = (_aTestMode != 0);
145 bool allFilesMode = (numItems == UInt32(-1));
146 if (allFilesMode)
147 numItems = _db.Items.Size();
148 if (numItems == 0)
149 return S_OK;
150 UInt32 i;
151 UInt64 totalSize = 0;
152 for (i = 0; i < numItems; i++)
153 {
154 const CItem &item = _db.Items[allFilesMode ? i : indices[i]];
155 if (!item.IsDir())
156 totalSize += item.Size;
157 }
158 RINOK(extractCallback->SetTotal(totalSize));
159
160 UInt64 currentTotalSize = 0, currentItemSize = 0;
161
162 CByteBuffer buf;
163 const UInt32 kBufSize = (1 << 16);
164 buf.SetCapacity(kBufSize);
165
166 for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
167 {
168 RINOK(extractCallback->SetCompleted(¤tTotalSize));
169 Int32 index = allFilesMode ? i : indices[i];
170 const CItem &item = _db.Items[index];
171 currentItemSize = 0;
172 if (!item.IsDir())
173 currentItemSize = item.Size;
174
175 CMyComPtr<ISequentialOutStream> realOutStream;
176 Int32 askMode = testMode ?
177 NArchive::NExtract::NAskMode::kTest :
178 NArchive::NExtract::NAskMode::kExtract;
179 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
180
181 if (item.IsDir())
182 {
183 RINOK(extractCallback->PrepareOperation(askMode));
184 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
185 continue;
186 }
187 if (!testMode && (!realOutStream))
188 continue;
189 RINOK(extractCallback->PrepareOperation(askMode));
190 UInt64 pos = 0;
191 int res = NArchive::NExtract::NOperationResult::kOK;
192 int i;
193 for (i = 0; i < item.Extents.Size(); i++)
194 {
195 if (item.Size == pos)
196 break;
197 if (res != NArchive::NExtract::NOperationResult::kOK)
198 break;
199 const CExtent &e = item.Extents[i];
200 RINOK(_stream->Seek((UInt64)e.Pos << _db.Header.BlockSizeLog, STREAM_SEEK_SET, NULL));
201 UInt64 extentSize = (UInt64)e.NumBlocks << _db.Header.BlockSizeLog;
202 for (;;)
203 {
204 if (extentSize == 0)
205 break;
206 UInt64 rem = item.Size - pos;
207 if (rem == 0)
208 {
209 if (extentSize >= (UInt64)((UInt32)1 << _db.Header.BlockSizeLog))
210 res = NArchive::NExtract::NOperationResult::kDataError;
211 break;
212 }
213 UInt32 curSize = kBufSize;
214 if (curSize > rem)
215 curSize = (UInt32)rem;
216 if (curSize > extentSize)
217 curSize = (UInt32)extentSize;
218 RINOK(ReadStream_FALSE(_stream, buf, curSize));
219 if (realOutStream)
220 {
221 RINOK(WriteStream(realOutStream, buf, curSize));
222 }
223 pos += curSize;
224 extentSize -= curSize;
225 UInt64 processed = currentTotalSize + pos;
226 RINOK(extractCallback->SetCompleted(&processed));
227 }
228 }
229 if (i != item.Extents.Size() || item.Size != pos)
230 res = NArchive::NExtract::NOperationResult::kDataError;
231 realOutStream.Release();
232 RINOK(extractCallback->SetOperationResult(res));
233 }
234 return S_OK;
235 COM_TRY_END
236 }
237
GetNumberOfItems(UInt32 * numItems)238 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
239 {
240 *numItems = _db.Items.Size();
241 return S_OK;
242 }
243
244 }}
245