1 // LzmaHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "Common/ComTry.h"
8 #include "Common/IntToString.h"
9
10 #include "Windows/PropVariant.h"
11
12 #include "../Common/CreateCoder.h"
13 #include "../Common/ProgressUtils.h"
14 #include "../Common/RegisterArc.h"
15 #include "../Common/StreamUtils.h"
16
17 #include "../Compress/LzmaDecoder.h"
18
19 #include "Common/DummyOutStream.h"
20
21 using namespace NWindows;
22
23 namespace NArchive {
24 namespace NLzma {
25
CheckDicSize(const Byte * p)26 static bool CheckDicSize(const Byte *p)
27 {
28 UInt32 dicSize = GetUi32(p);
29 for (int i = 1; i <= 30; i++)
30 if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i))
31 return true;
32 return (dicSize == 0xFFFFFFFF);
33 }
34
35 STATPROPSTG kProps[] =
36 {
37 { NULL, kpidSize, VT_UI8},
38 { NULL, kpidPackSize, VT_UI8},
39 { NULL, kpidMethod, VT_BSTR}
40 };
41
42 struct CHeader
43 {
44 UInt64 Size;
45 Byte FilterID;
46 Byte LzmaProps[5];
47
GetDicSizeNArchive::NLzma::CHeader48 UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); }
HasSizeNArchive::NLzma::CHeader49 bool HasSize() const { return (Size != (UInt64)(Int64)-1); }
50 bool Parse(const Byte *buf, bool isThereFilter);
51 };
52
Parse(const Byte * buf,bool isThereFilter)53 bool CHeader::Parse(const Byte *buf, bool isThereFilter)
54 {
55 FilterID = 0;
56 if (isThereFilter)
57 FilterID = buf[0];
58 const Byte *sig = buf + (isThereFilter ? 1 : 0);
59 for (int i = 0; i < 5; i++)
60 LzmaProps[i] = sig[i];
61 Size = GetUi64(sig + 5);
62 return
63 LzmaProps[0] < 5 * 5 * 9 &&
64 FilterID < 2 &&
65 (!HasSize() || Size < ((UInt64)1 << 56)) &&
66 CheckDicSize(LzmaProps + 1);
67 }
68
69 class CDecoder
70 {
71 NCompress::NLzma::CDecoder *_lzmaDecoderSpec;
72 CMyComPtr<ICompressCoder> _lzmaDecoder;
73 CMyComPtr<ISequentialOutStream> _bcjStream;
74 public:
75 ~CDecoder();
76 HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS
77 bool filtered, ISequentialInStream *inStream);
78
79 HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress);
80
GetInputProcessedSize() const81 UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); }
82
ReleaseInStream()83 void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); }
84
ReadInput(Byte * data,UInt32 size,UInt32 * processedSize)85 HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize)
86 { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); }
87 };
88
89 static const UInt64 k_BCJ = 0x03030103;
90
Create(DECL_EXTERNAL_CODECS_LOC_VARS bool filteredMode,ISequentialInStream * inStream)91 HRESULT CDecoder::Create(
92 DECL_EXTERNAL_CODECS_LOC_VARS
93 bool filteredMode, ISequentialInStream *inStream)
94 {
95 if (!_lzmaDecoder)
96 {
97 _lzmaDecoderSpec = new NCompress::NLzma::CDecoder;
98 _lzmaDecoder = _lzmaDecoderSpec;
99 }
100
101 if (filteredMode)
102 {
103 if (!_bcjStream)
104 {
105 CMyComPtr<ICompressCoder> coder;
106 RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_BCJ, coder, false));
107 if (!coder)
108 return E_NOTIMPL;
109 coder.QueryInterface(IID_ISequentialOutStream, &_bcjStream);
110 if (!_bcjStream)
111 return E_NOTIMPL;
112 }
113 }
114
115 return _lzmaDecoderSpec->SetInStream(inStream);
116 }
117
~CDecoder()118 CDecoder::~CDecoder()
119 {
120 ReleaseInStream();
121 }
122
Code(const CHeader & header,ISequentialOutStream * outStream,ICompressProgressInfo * progress)123 HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream,
124 ICompressProgressInfo *progress)
125 {
126 if (header.FilterID > 1)
127 return E_NOTIMPL;
128
129 {
130 CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
131 _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
132 if (!setDecoderProperties)
133 return E_NOTIMPL;
134 RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5));
135 }
136
137 CMyComPtr<ICompressSetOutStream> setOutStream;
138
139 bool filteredMode = (header.FilterID == 1);
140
141 if (filteredMode)
142 {
143 _bcjStream.QueryInterface(IID_ICompressSetOutStream, &setOutStream);
144 if (!setOutStream)
145 return E_NOTIMPL;
146 RINOK(setOutStream->SetOutStream(outStream));
147 outStream = _bcjStream;
148 }
149
150 const UInt64 *Size = header.HasSize() ? &header.Size : NULL;
151 HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress);
152
153 if (filteredMode)
154 {
155 CMyComPtr<IOutStreamFlush> flush;
156 _bcjStream.QueryInterface(IID_IOutStreamFlush, &flush);
157 if (flush)
158 {
159 HRESULT res2 = flush->Flush();
160 if (res == S_OK)
161 res = res2;
162 }
163 HRESULT res2 = setOutStream->ReleaseOutStream();
164 if (res == S_OK)
165 res = res2;
166 }
167 RINOK(res);
168
169 return S_OK;
170 }
171
172
173 class CHandler:
174 public IInArchive,
175 public IArchiveOpenSeq,
176 PUBLIC_ISetCompressCodecsInfo
177 public CMyUnknownImp
178 {
179 CHeader _header;
180 bool _lzma86;
181 UInt64 _startPosition;
182 UInt64 _packSize;
183 bool _packSizeDefined;
184 CMyComPtr<IInStream> _stream;
185 CMyComPtr<ISequentialInStream> _seqStream;
186
187 DECL_EXTERNAL_CODECS_VARS
188 DECL_ISetCompressCodecsInfo
189
190 public:
191 MY_QUERYINTERFACE_BEGIN2(IInArchive)
192 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq)
193 QUERY_ENTRY_ISetCompressCodecsInfo
194 MY_QUERYINTERFACE_END
195 MY_ADDREF_RELEASE
196
197 INTERFACE_IInArchive(;)
198 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
199
CHandler(bool lzma86)200 CHandler(bool lzma86) { _lzma86 = lzma86; }
201
GetHeaderSize() const202 unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); }
203
204 };
205
206 IMP_IInArchive_Props
207 IMP_IInArchive_ArcProps_NO_Table
208
GetArchiveProperty(PROPID propID,PROPVARIANT * value)209 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
210 {
211 NCOM::CPropVariant prop;
212 switch(propID)
213 {
214 case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
215 }
216 prop.Detach(value);
217 return S_OK;
218 }
219
GetNumberOfItems(UInt32 * numItems)220 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
221 {
222 *numItems = 1;
223 return S_OK;
224 }
225
DictSizeToString(UInt32 value,char * s)226 static void DictSizeToString(UInt32 value, char *s)
227 {
228 for (int i = 0; i <= 31; i++)
229 if ((UInt32(1) << i) == value)
230 {
231 ::ConvertUInt32ToString(i, s);
232 return;
233 }
234 char c = 'b';
235 if ((value & ((1 << 20) - 1)) == 0)
236 {
237 value >>= 20;
238 c = 'm';
239 }
240 else if ((value & ((1 << 10) - 1)) == 0)
241 {
242 value >>= 10;
243 c = 'k';
244 }
245 ::ConvertUInt32ToString(value, s);
246 int p = MyStringLen(s);
247 s[p++] = c;
248 s[p++] = '\0';
249 }
250
MyStrCat(char * d,const char * s)251 static void MyStrCat(char *d, const char *s)
252 {
253 MyStringCopy(d + MyStringLen(d), s);
254 }
255
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)256 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
257 {
258 NCOM::CPropVariant prop;
259 switch(propID)
260 {
261 case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break;
262 case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
263 case kpidMethod:
264 if (_stream)
265 {
266 char s[64];
267 s[0] = '\0';
268 if (_header.FilterID != 0)
269 MyStrCat(s, "BCJ ");
270 MyStrCat(s, "LZMA:");
271 DictSizeToString(_header.GetDicSize(), s + MyStringLen(s));
272 prop = s;
273 }
274 break;
275 }
276 prop.Detach(value);
277 return S_OK;
278 }
279
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)280 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *)
281 {
282 RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition));
283
284 const UInt32 kBufSize = 1 + 5 + 8 + 1;
285 Byte buf[kBufSize];
286
287 RINOK(ReadStream_FALSE(inStream, buf, kBufSize));
288
289 if (!_header.Parse(buf, _lzma86))
290 return S_FALSE;
291 const Byte *start = buf + GetHeaderSize();
292 if (start[0] != 0)
293 return S_FALSE;
294
295 UInt64 endPos;
296 RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos));
297 _packSize = endPos - _startPosition;
298 _packSizeDefined = true;
299
300 _stream = inStream;
301 _seqStream = inStream;
302 return S_OK;
303 }
304
OpenSeq(ISequentialInStream * stream)305 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
306 {
307 Close();
308 _seqStream = stream;
309 return S_OK;
310 }
311
Close()312 STDMETHODIMP CHandler::Close()
313 {
314 _packSizeDefined = false;
315 _stream.Release();
316 _seqStream.Release();
317 return S_OK;
318 }
319
320
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)321 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
322 Int32 testMode, IArchiveExtractCallback *extractCallback)
323 {
324 COM_TRY_BEGIN
325 if (numItems == 0)
326 return S_OK;
327 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
328 return E_INVALIDARG;
329
330 if (_stream)
331 extractCallback->SetTotal(_packSize);
332
333
334 CMyComPtr<ISequentialOutStream> realOutStream;
335 Int32 askMode = testMode ?
336 NExtract::NAskMode::kTest :
337 NExtract::NAskMode::kExtract;
338 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
339 if (!testMode && !realOutStream)
340 return S_OK;
341
342 extractCallback->PrepareOperation(askMode);
343
344 CDummyOutStream *outStreamSpec = new CDummyOutStream;
345 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
346 outStreamSpec->SetStream(realOutStream);
347 outStreamSpec->Init();
348 realOutStream.Release();
349
350 CLocalProgress *lps = new CLocalProgress;
351 CMyComPtr<ICompressProgressInfo> progress = lps;
352 lps->Init(extractCallback, true);
353
354 if (_stream)
355 {
356 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
357 }
358
359 CDecoder decoder;
360 HRESULT result = decoder.Create(
361 EXTERNAL_CODECS_VARS
362 _lzma86, _seqStream);
363 RINOK(result);
364
365 Int32 opRes = NExtract::NOperationResult::kOK;
366 bool firstItem = true;
367
368 for (;;)
369 {
370 lps->OutSize = outStreamSpec->GetSize();
371 lps->InSize = _packSize = decoder.GetInputProcessedSize();
372 _packSizeDefined = true;
373 RINOK(lps->SetCur());
374
375 CHeader st;
376
377 const UInt32 kBufSize = 1 + 5 + 8;
378 Byte buf[kBufSize];
379 const UInt32 headerSize = GetHeaderSize();
380 UInt32 processed;
381 RINOK(decoder.ReadInput(buf, headerSize, &processed));
382 if (processed != headerSize)
383 break;
384
385 if (!st.Parse(buf, _lzma86))
386 break;
387 firstItem = false;
388
389 result = decoder.Code(st, outStream, progress);
390 if (result == E_NOTIMPL)
391 {
392 opRes = NExtract::NOperationResult::kUnSupportedMethod;
393 break;
394 }
395 if (result == S_FALSE)
396 {
397 opRes = NExtract::NOperationResult::kDataError;
398 break;
399 }
400 RINOK(result);
401 }
402 if (firstItem)
403 return E_FAIL;
404 outStream.Release();
405 return extractCallback->SetOperationResult(opRes);
406 COM_TRY_END
407 }
408
409 IMPL_ISetCompressCodecsInfo
410
CreateArc()411 static IInArchive *CreateArc() { return new CHandler(false); }
CreateArc86()412 static IInArchive *CreateArc86() { return new CHandler(true); }
413
414 namespace NLzmaAr {
415
416 static CArcInfo g_ArcInfo =
417 { L"lzma", L"lzma", 0, 0xA, { 0 }, 0, true, CreateArc, NULL };
418 REGISTER_ARC(Lzma)
419
420 }
421
422 namespace NLzma86Ar {
423
424 static CArcInfo g_ArcInfo =
425 { L"lzma86", L"lzma86", 0, 0xB, { 0 }, 0, true, CreateArc86, NULL };
426 REGISTER_ARC(Lzma86)
427
428 }
429
430 }}
431