rlm@1
|
1 // RarHandler.cpp
|
rlm@1
|
2
|
rlm@1
|
3 #include "StdAfx.h"
|
rlm@1
|
4
|
rlm@1
|
5 #include "Common/ComTry.h"
|
rlm@1
|
6 #include "Common/IntToString.h"
|
rlm@1
|
7 #include "Common/StringConvert.h"
|
rlm@1
|
8
|
rlm@1
|
9 #include "Windows/PropVariant.h"
|
rlm@1
|
10 #include "Windows/Time.h"
|
rlm@1
|
11
|
rlm@1
|
12 #include "../../IPassword.h"
|
rlm@1
|
13
|
rlm@1
|
14 #include "../../Common/CreateCoder.h"
|
rlm@1
|
15 #include "../../Common/FilterCoder.h"
|
rlm@1
|
16 #include "../../Common/MethodId.h"
|
rlm@1
|
17 #include "../../Common/ProgressUtils.h"
|
rlm@1
|
18
|
rlm@1
|
19 #include "../../Compress/CopyCoder.h"
|
rlm@1
|
20
|
rlm@1
|
21 #include "../../Crypto/Rar20Crypto.h"
|
rlm@1
|
22 #include "../../Crypto/RarAes.h"
|
rlm@1
|
23
|
rlm@1
|
24 #include "../Common/ItemNameUtils.h"
|
rlm@1
|
25 #include "../Common/OutStreamWithCRC.h"
|
rlm@1
|
26
|
rlm@1
|
27 #include "RarHandler.h"
|
rlm@1
|
28
|
rlm@1
|
29 using namespace NWindows;
|
rlm@1
|
30 using namespace NTime;
|
rlm@1
|
31
|
rlm@1
|
32 namespace NArchive {
|
rlm@1
|
33 namespace NRar {
|
rlm@1
|
34
|
rlm@1
|
35 static const wchar_t *kHostOS[] =
|
rlm@1
|
36 {
|
rlm@1
|
37 L"MS DOS",
|
rlm@1
|
38 L"OS/2",
|
rlm@1
|
39 L"Win32",
|
rlm@1
|
40 L"Unix",
|
rlm@1
|
41 L"Mac OS",
|
rlm@1
|
42 L"BeOS"
|
rlm@1
|
43 };
|
rlm@1
|
44
|
rlm@1
|
45 static const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]);
|
rlm@1
|
46
|
rlm@1
|
47 static const wchar_t *kUnknownOS = L"Unknown";
|
rlm@1
|
48
|
rlm@1
|
49 STATPROPSTG kProps[] =
|
rlm@1
|
50 {
|
rlm@1
|
51 { NULL, kpidPath, VT_BSTR},
|
rlm@1
|
52 { NULL, kpidIsDir, VT_BOOL},
|
rlm@1
|
53 { NULL, kpidSize, VT_UI8},
|
rlm@1
|
54 { NULL, kpidPackSize, VT_UI8},
|
rlm@1
|
55 { NULL, kpidMTime, VT_FILETIME},
|
rlm@1
|
56 { NULL, kpidCTime, VT_FILETIME},
|
rlm@1
|
57 { NULL, kpidATime, VT_FILETIME},
|
rlm@1
|
58 { NULL, kpidAttrib, VT_UI4},
|
rlm@1
|
59
|
rlm@1
|
60 { NULL, kpidEncrypted, VT_BOOL},
|
rlm@1
|
61 { NULL, kpidSolid, VT_BOOL},
|
rlm@1
|
62 { NULL, kpidCommented, VT_BOOL},
|
rlm@1
|
63 { NULL, kpidSplitBefore, VT_BOOL},
|
rlm@1
|
64 { NULL, kpidSplitAfter, VT_BOOL},
|
rlm@1
|
65 { NULL, kpidCRC, VT_UI4},
|
rlm@1
|
66 { NULL, kpidHostOS, VT_BSTR},
|
rlm@1
|
67 { NULL, kpidMethod, VT_BSTR},
|
rlm@1
|
68 { NULL, kpidUnpackVer, VT_UI1}
|
rlm@1
|
69 };
|
rlm@1
|
70
|
rlm@1
|
71 STATPROPSTG kArcProps[] =
|
rlm@1
|
72 {
|
rlm@1
|
73 { NULL, kpidSolid, VT_BOOL},
|
rlm@1
|
74 { NULL, kpidNumBlocks, VT_UI4},
|
rlm@1
|
75 // { NULL, kpidEncrypted, VT_BOOL},
|
rlm@1
|
76 { NULL, kpidIsVolume, VT_BOOL},
|
rlm@1
|
77 { NULL, kpidNumVolumes, VT_UI4},
|
rlm@1
|
78 { NULL, kpidPhySize, VT_UI8}
|
rlm@1
|
79 // { NULL, kpidCommented, VT_BOOL}
|
rlm@1
|
80 };
|
rlm@1
|
81
|
rlm@1
|
82 IMP_IInArchive_Props
|
rlm@1
|
83 IMP_IInArchive_ArcProps
|
rlm@1
|
84
|
rlm@1
|
85 UInt64 CHandler::GetPackSize(int refIndex) const
|
rlm@1
|
86 {
|
rlm@1
|
87 const CRefItem &refItem = _refItems[refIndex];
|
rlm@1
|
88 UInt64 totalPackSize = 0;
|
rlm@1
|
89 for (int i = 0; i < refItem.NumItems; i++)
|
rlm@1
|
90 totalPackSize += _items[refItem.ItemIndex + i].PackSize;
|
rlm@1
|
91 return totalPackSize;
|
rlm@1
|
92 }
|
rlm@1
|
93
|
rlm@1
|
94 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
rlm@1
|
95 {
|
rlm@1
|
96 // COM_TRY_BEGIN
|
rlm@1
|
97 NWindows::NCOM::CPropVariant prop;
|
rlm@1
|
98 switch(propID)
|
rlm@1
|
99 {
|
rlm@1
|
100 case kpidSolid: prop = _archiveInfo.IsSolid(); break;
|
rlm@1
|
101 // case kpidEncrypted: prop = _archiveInfo.IsEncrypted(); break; // it's for encrypted names.
|
rlm@1
|
102 case kpidIsVolume: prop = _archiveInfo.IsVolume(); break;
|
rlm@1
|
103 case kpidNumVolumes: prop = (UInt32)_archives.Size(); break;
|
rlm@1
|
104 case kpidOffset: if (_archiveInfo.StartPosition != 0) prop = _archiveInfo.StartPosition; break;
|
rlm@1
|
105 // case kpidCommented: prop = _archiveInfo.IsCommented(); break;
|
rlm@1
|
106 case kpidNumBlocks:
|
rlm@1
|
107 {
|
rlm@1
|
108 UInt32 numBlocks = 0;
|
rlm@1
|
109 for (int i = 0; i < _refItems.Size(); i++)
|
rlm@1
|
110 if (!IsSolid(i))
|
rlm@1
|
111 numBlocks++;
|
rlm@1
|
112 prop = (UInt32)numBlocks;
|
rlm@1
|
113 break;
|
rlm@1
|
114 }
|
rlm@1
|
115 }
|
rlm@1
|
116 prop.Detach(value);
|
rlm@1
|
117 return S_OK;
|
rlm@1
|
118 // COM_TRY_END
|
rlm@1
|
119 }
|
rlm@1
|
120
|
rlm@1
|
121 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
rlm@1
|
122 {
|
rlm@1
|
123 *numItems = _refItems.Size();
|
rlm@1
|
124 return S_OK;
|
rlm@1
|
125 }
|
rlm@1
|
126
|
rlm@1
|
127 static bool RarTimeToFileTime(const CRarTime &rarTime, FILETIME &result)
|
rlm@1
|
128 {
|
rlm@1
|
129 if (!DosTimeToFileTime(rarTime.DosTime, result))
|
rlm@1
|
130 return false;
|
rlm@1
|
131 UInt64 value = (((UInt64)result.dwHighDateTime) << 32) + result.dwLowDateTime;
|
rlm@1
|
132 value += (UInt64)rarTime.LowSecond * 10000000;
|
rlm@1
|
133 value += ((UInt64)rarTime.SubTime[2] << 16) +
|
rlm@1
|
134 ((UInt64)rarTime.SubTime[1] << 8) +
|
rlm@1
|
135 ((UInt64)rarTime.SubTime[0]);
|
rlm@1
|
136 result.dwLowDateTime = (DWORD)value;
|
rlm@1
|
137 result.dwHighDateTime = DWORD(value >> 32);
|
rlm@1
|
138 return true;
|
rlm@1
|
139 }
|
rlm@1
|
140
|
rlm@1
|
141 static void RarTimeToProp(const CRarTime &rarTime, NWindows::NCOM::CPropVariant &prop)
|
rlm@1
|
142 {
|
rlm@1
|
143 FILETIME localFileTime, utcFileTime;
|
rlm@1
|
144 if (RarTimeToFileTime(rarTime, localFileTime))
|
rlm@1
|
145 {
|
rlm@1
|
146 if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))
|
rlm@1
|
147 utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
|
rlm@1
|
148 }
|
rlm@1
|
149 else
|
rlm@1
|
150 utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
|
rlm@1
|
151 prop = utcFileTime;
|
rlm@1
|
152 }
|
rlm@1
|
153
|
rlm@1
|
154 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
rlm@1
|
155 {
|
rlm@1
|
156 COM_TRY_BEGIN
|
rlm@1
|
157 NWindows::NCOM::CPropVariant prop;
|
rlm@1
|
158 const CRefItem &refItem = _refItems[index];
|
rlm@1
|
159 const CItemEx &item = _items[refItem.ItemIndex];
|
rlm@1
|
160 switch(propID)
|
rlm@1
|
161 {
|
rlm@1
|
162 case kpidPath:
|
rlm@1
|
163 {
|
rlm@1
|
164 UString u;
|
rlm@1
|
165 if (item.HasUnicodeName() && !item.UnicodeName.IsEmpty())
|
rlm@1
|
166 u = item.UnicodeName;
|
rlm@1
|
167 else
|
rlm@1
|
168 u = MultiByteToUnicodeString(item.Name, CP_OEMCP);
|
rlm@1
|
169 prop = (const wchar_t *)NItemName::WinNameToOSName(u);
|
rlm@1
|
170 break;
|
rlm@1
|
171 }
|
rlm@1
|
172 case kpidIsDir: prop = item.IsDir(); break;
|
rlm@1
|
173 case kpidSize: prop = item.Size; break;
|
rlm@1
|
174 case kpidPackSize: prop = GetPackSize(index); break;
|
rlm@1
|
175 case kpidMTime: RarTimeToProp(item.MTime, prop); break;
|
rlm@1
|
176 case kpidCTime: if (item.CTimeDefined) RarTimeToProp(item.CTime, prop); break;
|
rlm@1
|
177 case kpidATime: if (item.ATimeDefined) RarTimeToProp(item.ATime, prop); break;
|
rlm@1
|
178 case kpidAttrib: prop = item.GetWinAttributes(); break;
|
rlm@1
|
179 case kpidEncrypted: prop = item.IsEncrypted(); break;
|
rlm@1
|
180 case kpidSolid: prop = IsSolid(index); break;
|
rlm@1
|
181 case kpidCommented: prop = item.IsCommented(); break;
|
rlm@1
|
182 case kpidSplitBefore: prop = item.IsSplitBefore(); break;
|
rlm@1
|
183 case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break;
|
rlm@1
|
184 case kpidCRC:
|
rlm@1
|
185 {
|
rlm@1
|
186 const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
|
rlm@1
|
187 prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC);
|
rlm@1
|
188 break;
|
rlm@1
|
189 }
|
rlm@1
|
190 case kpidUnpackVer: prop = item.UnPackVersion; break;
|
rlm@1
|
191 case kpidMethod:
|
rlm@1
|
192 {
|
rlm@1
|
193 UString method;
|
rlm@1
|
194 if (item.Method >= Byte('0') && item.Method <= Byte('5'))
|
rlm@1
|
195 {
|
rlm@1
|
196 method = L"m";
|
rlm@1
|
197 wchar_t temp[32];
|
rlm@1
|
198 ConvertUInt64ToString(item.Method - Byte('0'), temp);
|
rlm@1
|
199 method += temp;
|
rlm@1
|
200 if (!item.IsDir())
|
rlm@1
|
201 {
|
rlm@1
|
202 method += L":";
|
rlm@1
|
203 ConvertUInt64ToString(16 + item.GetDictSize(), temp);
|
rlm@1
|
204 method += temp;
|
rlm@1
|
205 }
|
rlm@1
|
206 }
|
rlm@1
|
207 else
|
rlm@1
|
208 {
|
rlm@1
|
209 wchar_t temp[32];
|
rlm@1
|
210 ConvertUInt64ToString(item.Method, temp);
|
rlm@1
|
211 method += temp;
|
rlm@1
|
212 }
|
rlm@1
|
213 prop = method;
|
rlm@1
|
214 break;
|
rlm@1
|
215 }
|
rlm@1
|
216 case kpidHostOS: prop = (item.HostOS < kNumHostOSes) ? (kHostOS[item.HostOS]) : kUnknownOS; break;
|
rlm@1
|
217 }
|
rlm@1
|
218 prop.Detach(value);
|
rlm@1
|
219 return S_OK;
|
rlm@1
|
220 COM_TRY_END
|
rlm@1
|
221 }
|
rlm@1
|
222
|
rlm@1
|
223 class CVolumeName
|
rlm@1
|
224 {
|
rlm@1
|
225 bool _first;
|
rlm@1
|
226 bool _newStyle;
|
rlm@1
|
227 UString _unchangedPart;
|
rlm@1
|
228 UString _changedPart;
|
rlm@1
|
229 UString _afterPart;
|
rlm@1
|
230 public:
|
rlm@1
|
231 CVolumeName(): _newStyle(true) {};
|
rlm@1
|
232
|
rlm@1
|
233 bool InitName(const UString &name, bool newStyle)
|
rlm@1
|
234 {
|
rlm@1
|
235 _first = true;
|
rlm@1
|
236 _newStyle = newStyle;
|
rlm@1
|
237 int dotPos = name.ReverseFind('.');
|
rlm@1
|
238 UString basePart = name;
|
rlm@1
|
239 if (dotPos >= 0)
|
rlm@1
|
240 {
|
rlm@1
|
241 UString ext = name.Mid(dotPos + 1);
|
rlm@1
|
242 if (ext.CompareNoCase(L"rar") == 0)
|
rlm@1
|
243 {
|
rlm@1
|
244 _afterPart = name.Mid(dotPos);
|
rlm@1
|
245 basePart = name.Left(dotPos);
|
rlm@1
|
246 }
|
rlm@1
|
247 else if (ext.CompareNoCase(L"exe") == 0)
|
rlm@1
|
248 {
|
rlm@1
|
249 _afterPart = L".rar";
|
rlm@1
|
250 basePart = name.Left(dotPos);
|
rlm@1
|
251 }
|
rlm@1
|
252 else if (!_newStyle)
|
rlm@1
|
253 {
|
rlm@1
|
254 if (ext.CompareNoCase(L"000") == 0 || ext.CompareNoCase(L"001") == 0)
|
rlm@1
|
255 {
|
rlm@1
|
256 _afterPart.Empty();
|
rlm@1
|
257 _first = false;
|
rlm@1
|
258 _changedPart = ext;
|
rlm@1
|
259 _unchangedPart = name.Left(dotPos + 1);
|
rlm@1
|
260 return true;
|
rlm@1
|
261 }
|
rlm@1
|
262 }
|
rlm@1
|
263 }
|
rlm@1
|
264
|
rlm@1
|
265 if (!_newStyle)
|
rlm@1
|
266 {
|
rlm@1
|
267 _afterPart.Empty();
|
rlm@1
|
268 _unchangedPart = basePart + UString(L".");
|
rlm@1
|
269 _changedPart = L"r00";
|
rlm@1
|
270 return true;
|
rlm@1
|
271 }
|
rlm@1
|
272
|
rlm@1
|
273 int numLetters = 1;
|
rlm@1
|
274 if (basePart.Right(numLetters) == L"1" || basePart.Right(numLetters) == L"0")
|
rlm@1
|
275 {
|
rlm@1
|
276 while (numLetters < basePart.Length())
|
rlm@1
|
277 {
|
rlm@1
|
278 if (basePart[basePart.Length() - numLetters - 1] != '0')
|
rlm@1
|
279 break;
|
rlm@1
|
280 numLetters++;
|
rlm@1
|
281 }
|
rlm@1
|
282 }
|
rlm@1
|
283 else
|
rlm@1
|
284 return false;
|
rlm@1
|
285 _unchangedPart = basePart.Left(basePart.Length() - numLetters);
|
rlm@1
|
286 _changedPart = basePart.Right(numLetters);
|
rlm@1
|
287 return true;
|
rlm@1
|
288 }
|
rlm@1
|
289
|
rlm@1
|
290 UString GetNextName()
|
rlm@1
|
291 {
|
rlm@1
|
292 UString newName;
|
rlm@1
|
293 if (_newStyle || !_first)
|
rlm@1
|
294 {
|
rlm@1
|
295 int i;
|
rlm@1
|
296 int numLetters = _changedPart.Length();
|
rlm@1
|
297 for (i = numLetters - 1; i >= 0; i--)
|
rlm@1
|
298 {
|
rlm@1
|
299 wchar_t c = _changedPart[i];
|
rlm@1
|
300 if (c == L'9')
|
rlm@1
|
301 {
|
rlm@1
|
302 c = L'0';
|
rlm@1
|
303 newName = c + newName;
|
rlm@1
|
304 if (i == 0)
|
rlm@1
|
305 newName = UString(L'1') + newName;
|
rlm@1
|
306 continue;
|
rlm@1
|
307 }
|
rlm@1
|
308 c++;
|
rlm@1
|
309 newName = UString(c) + newName;
|
rlm@1
|
310 i--;
|
rlm@1
|
311 for (; i >= 0; i--)
|
rlm@1
|
312 newName = _changedPart[i] + newName;
|
rlm@1
|
313 break;
|
rlm@1
|
314 }
|
rlm@1
|
315 _changedPart = newName;
|
rlm@1
|
316 }
|
rlm@1
|
317 _first = false;
|
rlm@1
|
318 return _unchangedPart + _changedPart + _afterPart;
|
rlm@1
|
319 }
|
rlm@1
|
320 };
|
rlm@1
|
321
|
rlm@1
|
322 HRESULT CHandler::Open2(IInStream *stream,
|
rlm@1
|
323 const UInt64 *maxCheckStartPosition,
|
rlm@1
|
324 IArchiveOpenCallback *openArchiveCallback)
|
rlm@1
|
325 {
|
rlm@1
|
326 {
|
rlm@1
|
327 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
|
rlm@1
|
328 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
|
rlm@1
|
329 CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;
|
rlm@1
|
330
|
rlm@1
|
331 CVolumeName seqName;
|
rlm@1
|
332
|
rlm@1
|
333 UInt64 totalBytes = 0;
|
rlm@1
|
334 UInt64 curBytes = 0;
|
rlm@1
|
335
|
rlm@1
|
336 if (openArchiveCallback != NULL)
|
rlm@1
|
337 {
|
rlm@1
|
338 openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);
|
rlm@1
|
339 openArchiveCallbackWrap.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
|
rlm@1
|
340 }
|
rlm@1
|
341
|
rlm@1
|
342 for (;;)
|
rlm@1
|
343 {
|
rlm@1
|
344 CMyComPtr<IInStream> inStream;
|
rlm@1
|
345 if (!_archives.IsEmpty())
|
rlm@1
|
346 {
|
rlm@1
|
347 if (!openVolumeCallback)
|
rlm@1
|
348 break;
|
rlm@1
|
349
|
rlm@1
|
350 if(_archives.Size() == 1)
|
rlm@1
|
351 {
|
rlm@1
|
352 if (!_archiveInfo.IsVolume())
|
rlm@1
|
353 break;
|
rlm@1
|
354 UString baseName;
|
rlm@1
|
355 {
|
rlm@1
|
356 NCOM::CPropVariant prop;
|
rlm@1
|
357 RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
|
rlm@1
|
358 if (prop.vt != VT_BSTR)
|
rlm@1
|
359 break;
|
rlm@1
|
360 baseName = prop.bstrVal;
|
rlm@1
|
361 }
|
rlm@1
|
362 seqName.InitName(baseName, _archiveInfo.HaveNewVolumeName());
|
rlm@1
|
363 }
|
rlm@1
|
364
|
rlm@1
|
365 UString fullName = seqName.GetNextName();
|
rlm@1
|
366 HRESULT result = openVolumeCallback->GetStream(fullName, &inStream);
|
rlm@1
|
367 if (result == S_FALSE)
|
rlm@1
|
368 break;
|
rlm@1
|
369 if (result != S_OK)
|
rlm@1
|
370 return result;
|
rlm@1
|
371 if (!stream)
|
rlm@1
|
372 break;
|
rlm@1
|
373 }
|
rlm@1
|
374 else
|
rlm@1
|
375 inStream = stream;
|
rlm@1
|
376
|
rlm@1
|
377 UInt64 endPos = 0;
|
rlm@1
|
378 if (openArchiveCallback != NULL)
|
rlm@1
|
379 {
|
rlm@1
|
380 RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
|
rlm@1
|
381 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
|
rlm@1
|
382 totalBytes += endPos;
|
rlm@1
|
383 RINOK(openArchiveCallback->SetTotal(NULL, &totalBytes));
|
rlm@1
|
384 }
|
rlm@1
|
385
|
rlm@1
|
386 NArchive::NRar::CInArchive archive;
|
rlm@1
|
387 RINOK(archive.Open(inStream, maxCheckStartPosition));
|
rlm@1
|
388
|
rlm@1
|
389 if (_archives.IsEmpty())
|
rlm@1
|
390 archive.GetArchiveInfo(_archiveInfo);
|
rlm@1
|
391
|
rlm@1
|
392 CItemEx item;
|
rlm@1
|
393 for (;;)
|
rlm@1
|
394 {
|
rlm@1
|
395 HRESULT result = archive.GetNextItem(item, getTextPassword);
|
rlm@1
|
396 if (result == S_FALSE)
|
rlm@1
|
397 break;
|
rlm@1
|
398 RINOK(result);
|
rlm@1
|
399 if (item.IgnoreItem())
|
rlm@1
|
400 continue;
|
rlm@1
|
401
|
rlm@1
|
402 bool needAdd = true;
|
rlm@1
|
403 if (item.IsSplitBefore())
|
rlm@1
|
404 {
|
rlm@1
|
405 if (!_refItems.IsEmpty())
|
rlm@1
|
406 {
|
rlm@1
|
407 CRefItem &refItem = _refItems.Back();
|
rlm@1
|
408 refItem.NumItems++;
|
rlm@1
|
409 needAdd = false;
|
rlm@1
|
410 }
|
rlm@1
|
411 }
|
rlm@1
|
412 if (needAdd)
|
rlm@1
|
413 {
|
rlm@1
|
414 CRefItem refItem;
|
rlm@1
|
415 refItem.ItemIndex = _items.Size();
|
rlm@1
|
416 refItem.NumItems = 1;
|
rlm@1
|
417 refItem.VolumeIndex = _archives.Size();
|
rlm@1
|
418 _refItems.Add(refItem);
|
rlm@1
|
419 }
|
rlm@1
|
420 _items.Add(item);
|
rlm@1
|
421 if (openArchiveCallback != NULL && _items.Size() % 100 == 0)
|
rlm@1
|
422 {
|
rlm@1
|
423 UInt64 numFiles = _items.Size();
|
rlm@1
|
424 UInt64 numBytes = curBytes + item.Position;
|
rlm@1
|
425 RINOK(openArchiveCallback->SetCompleted(&numFiles, &numBytes));
|
rlm@1
|
426 }
|
rlm@1
|
427 }
|
rlm@1
|
428 curBytes += endPos;
|
rlm@1
|
429 _archives.Add(archive);
|
rlm@1
|
430 }
|
rlm@1
|
431 }
|
rlm@1
|
432 return S_OK;
|
rlm@1
|
433 }
|
rlm@1
|
434
|
rlm@1
|
435 STDMETHODIMP CHandler::Open(IInStream *stream,
|
rlm@1
|
436 const UInt64 *maxCheckStartPosition,
|
rlm@1
|
437 IArchiveOpenCallback *openArchiveCallback)
|
rlm@1
|
438 {
|
rlm@1
|
439 COM_TRY_BEGIN
|
rlm@1
|
440 Close();
|
rlm@1
|
441 try
|
rlm@1
|
442 {
|
rlm@1
|
443 HRESULT res = Open2(stream, maxCheckStartPosition, openArchiveCallback);
|
rlm@1
|
444 if (res != S_OK)
|
rlm@1
|
445 Close();
|
rlm@1
|
446 return res;
|
rlm@1
|
447 }
|
rlm@1
|
448 catch(const CInArchiveException &) { Close(); return S_FALSE; }
|
rlm@1
|
449 catch(...) { Close(); throw; }
|
rlm@1
|
450 COM_TRY_END
|
rlm@1
|
451 }
|
rlm@1
|
452
|
rlm@1
|
453 STDMETHODIMP CHandler::Close()
|
rlm@1
|
454 {
|
rlm@1
|
455 COM_TRY_BEGIN
|
rlm@1
|
456 _refItems.Clear();
|
rlm@1
|
457 _items.Clear();
|
rlm@1
|
458 _archives.Clear();
|
rlm@1
|
459 return S_OK;
|
rlm@1
|
460 COM_TRY_END
|
rlm@1
|
461 }
|
rlm@1
|
462
|
rlm@1
|
463 struct CMethodItem
|
rlm@1
|
464 {
|
rlm@1
|
465 Byte RarUnPackVersion;
|
rlm@1
|
466 CMyComPtr<ICompressCoder> Coder;
|
rlm@1
|
467 };
|
rlm@1
|
468
|
rlm@1
|
469
|
rlm@1
|
470 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
rlm@1
|
471 Int32 _aTestMode, IArchiveExtractCallback *_anExtractCallback)
|
rlm@1
|
472 {
|
rlm@1
|
473 COM_TRY_BEGIN
|
rlm@1
|
474 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
|
rlm@1
|
475 bool testMode = (_aTestMode != 0);
|
rlm@1
|
476 CMyComPtr<IArchiveExtractCallback> extractCallback = _anExtractCallback;
|
rlm@1
|
477 UInt64 censoredTotalUnPacked = 0,
|
rlm@1
|
478 // censoredTotalPacked = 0,
|
rlm@1
|
479 importantTotalUnPacked = 0;
|
rlm@1
|
480 // importantTotalPacked = 0;
|
rlm@1
|
481 bool allFilesMode = (numItems == UInt32(-1));
|
rlm@1
|
482 if (allFilesMode)
|
rlm@1
|
483 numItems = _refItems.Size();
|
rlm@1
|
484 if(numItems == 0)
|
rlm@1
|
485 return S_OK;
|
rlm@1
|
486 int lastIndex = 0;
|
rlm@1
|
487 CRecordVector<int> importantIndexes;
|
rlm@1
|
488 CRecordVector<bool> extractStatuses;
|
rlm@1
|
489
|
rlm@1
|
490 for(UInt32 t = 0; t < numItems; t++)
|
rlm@1
|
491 {
|
rlm@1
|
492 int index = allFilesMode ? t : indices[t];
|
rlm@1
|
493 const CRefItem &refItem = _refItems[index];
|
rlm@1
|
494 const CItemEx &item = _items[refItem.ItemIndex];
|
rlm@1
|
495 censoredTotalUnPacked += item.Size;
|
rlm@1
|
496 // censoredTotalPacked += item.PackSize;
|
rlm@1
|
497 int j;
|
rlm@1
|
498 for(j = lastIndex; j <= index; j++)
|
rlm@1
|
499 // if(!_items[_refItems[j].ItemIndex].IsSolid())
|
rlm@1
|
500 if(!IsSolid(j))
|
rlm@1
|
501 lastIndex = j;
|
rlm@1
|
502 for(j = lastIndex; j <= index; j++)
|
rlm@1
|
503 {
|
rlm@1
|
504 const CRefItem &refItem = _refItems[j];
|
rlm@1
|
505 const CItemEx &item = _items[refItem.ItemIndex];
|
rlm@1
|
506
|
rlm@1
|
507 // const CItemEx &item = _items[j];
|
rlm@1
|
508
|
rlm@1
|
509 importantTotalUnPacked += item.Size;
|
rlm@1
|
510 // importantTotalPacked += item.PackSize;
|
rlm@1
|
511 importantIndexes.Add(j);
|
rlm@1
|
512 extractStatuses.Add(j == index);
|
rlm@1
|
513 }
|
rlm@1
|
514 lastIndex = index + 1;
|
rlm@1
|
515 }
|
rlm@1
|
516
|
rlm@1
|
517 extractCallback->SetTotal(importantTotalUnPacked);
|
rlm@1
|
518 UInt64 currentImportantTotalUnPacked = 0;
|
rlm@1
|
519 UInt64 currentImportantTotalPacked = 0;
|
rlm@1
|
520 UInt64 currentUnPackSize, currentPackSize;
|
rlm@1
|
521
|
rlm@1
|
522 CObjectVector<CMethodItem> methodItems;
|
rlm@1
|
523
|
rlm@1
|
524 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
|
rlm@1
|
525 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
rlm@1
|
526
|
rlm@1
|
527 CFilterCoder *filterStreamSpec = new CFilterCoder;
|
rlm@1
|
528 CMyComPtr<ISequentialInStream> filterStream = filterStreamSpec;
|
rlm@1
|
529
|
rlm@1
|
530 NCrypto::NRar20::CDecoder *rar20CryptoDecoderSpec = NULL;
|
rlm@1
|
531 CMyComPtr<ICompressFilter> rar20CryptoDecoder;
|
rlm@1
|
532 NCrypto::NRar29::CDecoder *rar29CryptoDecoderSpec = NULL;
|
rlm@1
|
533 CMyComPtr<ICompressFilter> rar29CryptoDecoder;
|
rlm@1
|
534
|
rlm@1
|
535 CFolderInStream *folderInStreamSpec = NULL;
|
rlm@1
|
536 CMyComPtr<ISequentialInStream> folderInStream;
|
rlm@1
|
537
|
rlm@1
|
538 CLocalProgress *lps = new CLocalProgress;
|
rlm@1
|
539 CMyComPtr<ICompressProgressInfo> progress = lps;
|
rlm@1
|
540 lps->Init(extractCallback, false);
|
rlm@1
|
541
|
rlm@1
|
542 bool solidStart = true;
|
rlm@1
|
543 for(int i = 0; i < importantIndexes.Size(); i++,
|
rlm@1
|
544 currentImportantTotalUnPacked += currentUnPackSize,
|
rlm@1
|
545 currentImportantTotalPacked += currentPackSize)
|
rlm@1
|
546 {
|
rlm@1
|
547 lps->InSize = currentImportantTotalPacked;
|
rlm@1
|
548 lps->OutSize = currentImportantTotalUnPacked;
|
rlm@1
|
549 RINOK(lps->SetCur());
|
rlm@1
|
550 CMyComPtr<ISequentialOutStream> realOutStream;
|
rlm@1
|
551
|
rlm@1
|
552 Int32 askMode;
|
rlm@1
|
553 if(extractStatuses[i])
|
rlm@1
|
554 askMode = testMode ?
|
rlm@1
|
555 NArchive::NExtract::NAskMode::kTest :
|
rlm@1
|
556 NArchive::NExtract::NAskMode::kExtract;
|
rlm@1
|
557 else
|
rlm@1
|
558 askMode = NArchive::NExtract::NAskMode::kSkip;
|
rlm@1
|
559
|
rlm@1
|
560 UInt32 index = importantIndexes[i];
|
rlm@1
|
561
|
rlm@1
|
562 const CRefItem &refItem = _refItems[index];
|
rlm@1
|
563 const CItemEx &item = _items[refItem.ItemIndex];
|
rlm@1
|
564
|
rlm@1
|
565 currentUnPackSize = item.Size;
|
rlm@1
|
566
|
rlm@1
|
567 currentPackSize = GetPackSize(index);
|
rlm@1
|
568
|
rlm@1
|
569 if(item.IgnoreItem())
|
rlm@1
|
570 continue;
|
rlm@1
|
571
|
rlm@1
|
572 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
rlm@1
|
573
|
rlm@1
|
574 if (!IsSolid(index))
|
rlm@1
|
575 solidStart = true;
|
rlm@1
|
576 if(item.IsDir())
|
rlm@1
|
577 {
|
rlm@1
|
578 RINOK(extractCallback->PrepareOperation(askMode));
|
rlm@1
|
579 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
|
rlm@1
|
580 continue;
|
rlm@1
|
581 }
|
rlm@1
|
582
|
rlm@1
|
583 bool mustBeProcessedAnywhere = false;
|
rlm@1
|
584 if(i < importantIndexes.Size() - 1)
|
rlm@1
|
585 {
|
rlm@1
|
586 // const CRefItem &nextRefItem = _refItems[importantIndexes[i + 1]];
|
rlm@1
|
587 // const CItemEx &nextItemInfo = _items[nextRefItem.ItemIndex];
|
rlm@1
|
588 // mustBeProcessedAnywhere = nextItemInfo.IsSolid();
|
rlm@1
|
589 mustBeProcessedAnywhere = IsSolid(importantIndexes[i + 1]);
|
rlm@1
|
590 }
|
rlm@1
|
591
|
rlm@1
|
592 if (!mustBeProcessedAnywhere && !testMode && !realOutStream)
|
rlm@1
|
593 continue;
|
rlm@1
|
594
|
rlm@1
|
595 if (!realOutStream && !testMode)
|
rlm@1
|
596 askMode = NArchive::NExtract::NAskMode::kSkip;
|
rlm@1
|
597
|
rlm@1
|
598 RINOK(extractCallback->PrepareOperation(askMode));
|
rlm@1
|
599
|
rlm@1
|
600 COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
|
rlm@1
|
601 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
|
rlm@1
|
602 outStreamSpec->SetStream(realOutStream);
|
rlm@1
|
603 outStreamSpec->Init();
|
rlm@1
|
604 realOutStream.Release();
|
rlm@1
|
605
|
rlm@1
|
606 /*
|
rlm@1
|
607 for (int partIndex = 0; partIndex < 1; partIndex++)
|
rlm@1
|
608 {
|
rlm@1
|
609 CMyComPtr<ISequentialInStream> inStream;
|
rlm@1
|
610
|
rlm@1
|
611 // item redefinition
|
rlm@1
|
612 const CItemEx &item = _items[refItem.ItemIndex + partIndex];
|
rlm@1
|
613
|
rlm@1
|
614 NArchive::NRar::CInArchive &archive = _archives[refItem.VolumeIndex + partIndex];
|
rlm@1
|
615
|
rlm@1
|
616 inStream.Attach(archive.CreateLimitedStream(item.GetDataPosition(),
|
rlm@1
|
617 item.PackSize));
|
rlm@1
|
618 */
|
rlm@1
|
619 if (!folderInStream)
|
rlm@1
|
620 {
|
rlm@1
|
621 folderInStreamSpec = new CFolderInStream;
|
rlm@1
|
622 folderInStream = folderInStreamSpec;
|
rlm@1
|
623 }
|
rlm@1
|
624
|
rlm@1
|
625 folderInStreamSpec->Init(&_archives, &_items, refItem);
|
rlm@1
|
626
|
rlm@1
|
627 UInt64 packSize = currentPackSize;
|
rlm@1
|
628
|
rlm@1
|
629 // packedPos += item.PackSize;
|
rlm@1
|
630 // unpackedPos += 0;
|
rlm@1
|
631
|
rlm@1
|
632 CMyComPtr<ISequentialInStream> inStream;
|
rlm@1
|
633 if (item.IsEncrypted())
|
rlm@1
|
634 {
|
rlm@1
|
635 CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
|
rlm@1
|
636 if (item.UnPackVersion >= 29)
|
rlm@1
|
637 {
|
rlm@1
|
638 if (!rar29CryptoDecoder)
|
rlm@1
|
639 {
|
rlm@1
|
640 rar29CryptoDecoderSpec = new NCrypto::NRar29::CDecoder;
|
rlm@1
|
641 rar29CryptoDecoder = rar29CryptoDecoderSpec;
|
rlm@1
|
642 // RINOK(rar29CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar29Decoder));
|
rlm@1
|
643 }
|
rlm@1
|
644 rar29CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36);
|
rlm@1
|
645 CMyComPtr<ICompressSetDecoderProperties2> cryptoProperties;
|
rlm@1
|
646 RINOK(rar29CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2,
|
rlm@1
|
647 &cryptoProperties));
|
rlm@1
|
648 RINOK(cryptoProperties->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0));
|
rlm@1
|
649 filterStreamSpec->Filter = rar29CryptoDecoder;
|
rlm@1
|
650 }
|
rlm@1
|
651 else if (item.UnPackVersion >= 20)
|
rlm@1
|
652 {
|
rlm@1
|
653 if (!rar20CryptoDecoder)
|
rlm@1
|
654 {
|
rlm@1
|
655 rar20CryptoDecoderSpec = new NCrypto::NRar20::CDecoder;
|
rlm@1
|
656 rar20CryptoDecoder = rar20CryptoDecoderSpec;
|
rlm@1
|
657 // RINOK(rar20CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar20Decoder));
|
rlm@1
|
658 }
|
rlm@1
|
659 filterStreamSpec->Filter = rar20CryptoDecoder;
|
rlm@1
|
660 }
|
rlm@1
|
661 else
|
rlm@1
|
662 {
|
rlm@1
|
663 outStream.Release();
|
rlm@1
|
664 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
|
rlm@1
|
665 continue;
|
rlm@1
|
666 }
|
rlm@1
|
667 RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword,
|
rlm@1
|
668 &cryptoSetPassword));
|
rlm@1
|
669
|
rlm@1
|
670 if (!getTextPassword)
|
rlm@1
|
671 extractCallback.QueryInterface(IID_ICryptoGetTextPassword,
|
rlm@1
|
672 &getTextPassword);
|
rlm@1
|
673 if (getTextPassword)
|
rlm@1
|
674 {
|
rlm@1
|
675 CMyComBSTR password;
|
rlm@1
|
676 RINOK(getTextPassword->CryptoGetTextPassword(&password));
|
rlm@1
|
677 if (item.UnPackVersion >= 29)
|
rlm@1
|
678 {
|
rlm@1
|
679 CByteBuffer buffer;
|
rlm@1
|
680 UString unicodePassword(password);
|
rlm@1
|
681 const UInt32 sizeInBytes = unicodePassword.Length() * 2;
|
rlm@1
|
682 buffer.SetCapacity(sizeInBytes);
|
rlm@1
|
683 for (int i = 0; i < unicodePassword.Length(); i++)
|
rlm@1
|
684 {
|
rlm@1
|
685 wchar_t c = unicodePassword[i];
|
rlm@1
|
686 ((Byte *)buffer)[i * 2] = (Byte)c;
|
rlm@1
|
687 ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
|
rlm@1
|
688 }
|
rlm@1
|
689 RINOK(cryptoSetPassword->CryptoSetPassword(
|
rlm@1
|
690 (const Byte *)buffer, sizeInBytes));
|
rlm@1
|
691 }
|
rlm@1
|
692 else
|
rlm@1
|
693 {
|
rlm@1
|
694 AString oemPassword = UnicodeStringToMultiByte(
|
rlm@1
|
695 (const wchar_t *)password, CP_OEMCP);
|
rlm@1
|
696 RINOK(cryptoSetPassword->CryptoSetPassword(
|
rlm@1
|
697 (const Byte *)(const char *)oemPassword, oemPassword.Length()));
|
rlm@1
|
698 }
|
rlm@1
|
699 }
|
rlm@1
|
700 else
|
rlm@1
|
701 {
|
rlm@1
|
702 RINOK(cryptoSetPassword->CryptoSetPassword(0, 0));
|
rlm@1
|
703 }
|
rlm@1
|
704 filterStreamSpec->SetInStream(folderInStream);
|
rlm@1
|
705 inStream = filterStream;
|
rlm@1
|
706 }
|
rlm@1
|
707 else
|
rlm@1
|
708 {
|
rlm@1
|
709 inStream = folderInStream;
|
rlm@1
|
710 }
|
rlm@1
|
711 CMyComPtr<ICompressCoder> commonCoder;
|
rlm@1
|
712 switch(item.Method)
|
rlm@1
|
713 {
|
rlm@1
|
714 case '0':
|
rlm@1
|
715 {
|
rlm@1
|
716 commonCoder = copyCoder;
|
rlm@1
|
717 break;
|
rlm@1
|
718 }
|
rlm@1
|
719 case '1':
|
rlm@1
|
720 case '2':
|
rlm@1
|
721 case '3':
|
rlm@1
|
722 case '4':
|
rlm@1
|
723 case '5':
|
rlm@1
|
724 {
|
rlm@1
|
725 /*
|
rlm@1
|
726 if (item.UnPackVersion >= 29)
|
rlm@1
|
727 {
|
rlm@1
|
728 outStream.Release();
|
rlm@1
|
729 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
|
rlm@1
|
730 continue;
|
rlm@1
|
731 }
|
rlm@1
|
732 */
|
rlm@1
|
733 int m;
|
rlm@1
|
734 for (m = 0; m < methodItems.Size(); m++)
|
rlm@1
|
735 if (methodItems[m].RarUnPackVersion == item.UnPackVersion)
|
rlm@1
|
736 break;
|
rlm@1
|
737 if (m == methodItems.Size())
|
rlm@1
|
738 {
|
rlm@1
|
739 CMethodItem mi;
|
rlm@1
|
740 mi.RarUnPackVersion = item.UnPackVersion;
|
rlm@1
|
741
|
rlm@1
|
742 mi.Coder.Release();
|
rlm@1
|
743 if (item.UnPackVersion <= 30)
|
rlm@1
|
744 {
|
rlm@1
|
745 UInt32 methodID = 0x040300;
|
rlm@1
|
746 if (item.UnPackVersion < 20)
|
rlm@1
|
747 methodID += 1;
|
rlm@1
|
748 else if (item.UnPackVersion < 29)
|
rlm@1
|
749 methodID += 2;
|
rlm@1
|
750 else
|
rlm@1
|
751 methodID += 3;
|
rlm@1
|
752 RINOK(CreateCoder(EXTERNAL_CODECS_VARS methodID, mi.Coder, false));
|
rlm@1
|
753 }
|
rlm@1
|
754
|
rlm@1
|
755 if (mi.Coder == 0)
|
rlm@1
|
756 {
|
rlm@1
|
757 outStream.Release();
|
rlm@1
|
758 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
|
rlm@1
|
759 continue;
|
rlm@1
|
760 }
|
rlm@1
|
761
|
rlm@1
|
762 m = methodItems.Add(mi);
|
rlm@1
|
763 }
|
rlm@1
|
764 CMyComPtr<ICompressCoder> decoder = methodItems[m].Coder;
|
rlm@1
|
765
|
rlm@1
|
766 CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;
|
rlm@1
|
767 RINOK(decoder.QueryInterface(IID_ICompressSetDecoderProperties2,
|
rlm@1
|
768 &compressSetDecoderProperties));
|
rlm@1
|
769
|
rlm@1
|
770 Byte isSolid = (Byte)((IsSolid(index) || item.IsSplitBefore()) ? 1: 0);
|
rlm@1
|
771 if (solidStart)
|
rlm@1
|
772 {
|
rlm@1
|
773 isSolid = false;
|
rlm@1
|
774 solidStart = false;
|
rlm@1
|
775 }
|
rlm@1
|
776
|
rlm@1
|
777
|
rlm@1
|
778 RINOK(compressSetDecoderProperties->SetDecoderProperties2(&isSolid, 1));
|
rlm@1
|
779
|
rlm@1
|
780 commonCoder = decoder;
|
rlm@1
|
781 break;
|
rlm@1
|
782 }
|
rlm@1
|
783 default:
|
rlm@1
|
784 outStream.Release();
|
rlm@1
|
785 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
|
rlm@1
|
786 continue;
|
rlm@1
|
787 }
|
rlm@1
|
788 HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &item.Size, progress);
|
rlm@1
|
789 if (item.IsEncrypted())
|
rlm@1
|
790 filterStreamSpec->ReleaseInStream();
|
rlm@1
|
791 if (result == S_FALSE)
|
rlm@1
|
792 {
|
rlm@1
|
793 outStream.Release();
|
rlm@1
|
794 RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kDataError));
|
rlm@1
|
795 continue;
|
rlm@1
|
796 }
|
rlm@1
|
797 if (result != S_OK)
|
rlm@1
|
798 return result;
|
rlm@1
|
799
|
rlm@1
|
800 /*
|
rlm@1
|
801 if (refItem.NumItems == 1 &&
|
rlm@1
|
802 !item.IsSplitBefore() && !item.IsSplitAfter())
|
rlm@1
|
803 */
|
rlm@1
|
804 {
|
rlm@1
|
805 const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1];
|
rlm@1
|
806 bool crcOK = outStreamSpec->GetCRC() == lastItem.FileCRC;
|
rlm@1
|
807 outStream.Release();
|
rlm@1
|
808 RINOK(extractCallback->SetOperationResult(crcOK ? NArchive::NExtract::NOperationResult::kOK:
|
rlm@1
|
809 NArchive::NExtract::NOperationResult::kCRCError));
|
rlm@1
|
810 }
|
rlm@1
|
811 /*
|
rlm@1
|
812 else
|
rlm@1
|
813 {
|
rlm@1
|
814 bool crcOK = true;
|
rlm@1
|
815 for (int partIndex = 0; partIndex < refItem.NumItems; partIndex++)
|
rlm@1
|
816 {
|
rlm@1
|
817 const CItemEx &item = _items[refItem.ItemIndex + partIndex];
|
rlm@1
|
818 if (item.FileCRC != folderInStreamSpec->CRCs[partIndex])
|
rlm@1
|
819 {
|
rlm@1
|
820 crcOK = false;
|
rlm@1
|
821 break;
|
rlm@1
|
822 }
|
rlm@1
|
823 }
|
rlm@1
|
824 RINOK(extractCallback->SetOperationResult(crcOK ? NArchive::NExtract::NOperationResult::kOK:
|
rlm@1
|
825 NArchive::NExtract::NOperationResult::kCRCError));
|
rlm@1
|
826 }
|
rlm@1
|
827 */
|
rlm@1
|
828 }
|
rlm@1
|
829 return S_OK;
|
rlm@1
|
830 COM_TRY_END
|
rlm@1
|
831 }
|
rlm@1
|
832
|
rlm@1
|
833 IMPL_ISetCompressCodecsInfo
|
rlm@1
|
834
|
rlm@1
|
835 }}
|