blob: e4029e07aeb807b9f5f34601f775644b87f1f5c3 [file] [log] [blame]
repo syncbaa38582013-07-26 17:53:31 -07001// XzHandler.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6#include "../../../C/XzCrc64.h"
7#include "../../../C/XzEnc.h"
8
9#include "../../Common/ComTry.h"
10#include "../../Common/IntToString.h"
11
12#include "../ICoder.h"
13
14#include "../Common/CWrappers.h"
15#include "../Common/ProgressUtils.h"
16#include "../Common/RegisterArc.h"
17#include "../Common/StreamUtils.h"
18
19#include "../Compress/CopyCoder.h"
20
21#include "IArchive.h"
22
23#include "Common/HandlerOut.h"
24
25using namespace NWindows;
26
27namespace NCompress {
28namespace NLzma2 {
29
30HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props);
31
32}}
33
34static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
35static void SzFree(void *, void *address) { MyFree(address); }
36static ISzAlloc g_Alloc = { SzAlloc, SzFree };
37
38namespace NArchive {
39namespace NXz {
40
41struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
42
43class CHandler:
44 public IInArchive,
45 public IArchiveOpenSeq,
46 #ifndef EXTRACT_ONLY
47 public IOutArchive,
48 public ISetProperties,
49 public COutHandler,
50 #endif
51 public CMyUnknownImp
52{
53 Int64 _startPosition;
54 UInt64 _packSize;
55 UInt64 _unpackSize;
56 UInt64 _numBlocks;
57 AString _methodsString;
58 bool _useSeq;
59 UInt64 _unpackSizeDefined;
60 UInt64 _packSizeDefined;
61
62 CMyComPtr<IInStream> _stream;
63 CMyComPtr<ISequentialInStream> _seqStream;
64
65 UInt32 _crcSize;
66
67 void Init()
68 {
69 _crcSize = 4;
70 COutHandler::Init();
71 }
72
73 HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
74
75public:
76 MY_QUERYINTERFACE_BEGIN2(IInArchive)
77 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq)
78 #ifndef EXTRACT_ONLY
79 MY_QUERYINTERFACE_ENTRY(IOutArchive)
80 MY_QUERYINTERFACE_ENTRY(ISetProperties)
81 #endif
82 MY_QUERYINTERFACE_END
83 MY_ADDREF_RELEASE
84
85 INTERFACE_IInArchive(;)
86 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
87
88 #ifndef EXTRACT_ONLY
89 INTERFACE_IOutArchive(;)
90 STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps);
91 #endif
92
93 CHandler();
94};
95
96CHandler::CHandler()
97{
98 Init();
99}
100
101STATPROPSTG kProps[] =
102{
103 { NULL, kpidSize, VT_UI8},
104 { NULL, kpidPackSize, VT_UI8},
105 { NULL, kpidMethod, VT_BSTR}
106};
107
108STATPROPSTG kArcProps[] =
109{
110 { NULL, kpidMethod, VT_BSTR},
111 { NULL, kpidNumBlocks, VT_UI4}
112};
113
114IMP_IInArchive_Props
115IMP_IInArchive_ArcProps
116
117static char GetHex(Byte value)
118{
119 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
120}
121
122static inline void AddHexToString(AString &res, Byte value)
123{
124 res += GetHex((Byte)(value >> 4));
125 res += GetHex((Byte)(value & 0xF));
126}
127
128static AString ConvertUInt32ToString(UInt32 value)
129{
130 char temp[32];
131 ::ConvertUInt32ToString(value, temp);
132 return temp;
133}
134
135static AString Lzma2PropToString(int prop)
136{
137 if ((prop & 1) == 0)
138 return ConvertUInt32ToString(prop / 2 + 12);
139 AString res;
140 char c;
141
142 UInt32 size = (2 | ((prop) & 1)) << ((prop) / 2 + 1);
143
144 if (prop > 17)
145 {
146 res = ConvertUInt32ToString(size >> 10);
147 c = 'm';
148 }
149 else
150 {
151 res = ConvertUInt32ToString(size);
152 c = 'k';
153 }
154 return res + c;
155}
156
157struct CMethodNamePair
158{
159 UInt32 Id;
160 const char *Name;
161};
162
163static CMethodNamePair g_NamePairs[] =
164{
165 { XZ_ID_Subblock, "SB" },
166 { XZ_ID_Delta, "Delta" },
167 { XZ_ID_X86, "x86" },
168 { XZ_ID_PPC, "PPC" },
169 { XZ_ID_IA64, "IA64" },
170 { XZ_ID_ARM, "ARM" },
171 { XZ_ID_ARMT, "ARMT" },
172 { XZ_ID_SPARC, "SPARC" },
173 { XZ_ID_LZMA2, "LZMA2" }
174};
175
176static AString GetMethodString(const CXzFilter &f)
177{
178 AString s;
179
180 for (int i = 0; i < sizeof(g_NamePairs) / sizeof(g_NamePairs[i]); i++)
181 if (g_NamePairs[i].Id == f.id)
182 s = g_NamePairs[i].Name;
183 if (s.IsEmpty())
184 {
185 char temp[32];
186 ::ConvertUInt64ToString(f.id, temp);
187 s = temp;
188 }
189
190 if (f.propsSize > 0)
191 {
192 s += ':';
193 if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
194 s += Lzma2PropToString(f.props[0]);
195 else if (f.id == XZ_ID_Delta && f.propsSize == 1)
196 s += ConvertUInt32ToString((UInt32)f.props[0] + 1);
197 else
198 {
199 s += '[';
200 for (UInt32 bi = 0; bi < f.propsSize; bi++)
201 AddHexToString(s, f.props[bi]);
202 s += ']';
203 }
204 }
205 return s;
206}
207
208static void AddString(AString &dest, const AString &src)
209{
210 if (!dest.IsEmpty())
211 dest += ' ';
212 dest += src;
213}
214
215static const char *kChecks[] =
216{
217 "NoCheck",
218 "CRC32",
219 NULL,
220 NULL,
221 "CRC64",
222 NULL,
223 NULL,
224 NULL,
225 NULL,
226 NULL,
227 "SHA256",
228 NULL,
229 NULL,
230 NULL,
231 NULL,
232 NULL
233};
234
235static AString GetCheckString(const CXzs &xzs)
236{
237 size_t i;
238 UInt32 mask = 0;
239 for (i = 0; i < xzs.num; i++)
240 mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
241 AString s;
242 for (i = 0; i <= XZ_CHECK_MASK; i++)
243 if (((mask >> i) & 1) != 0)
244 {
245 AString s2;
246 if (kChecks[i])
247 s2 = kChecks[i];
248 else
249 s2 = "Check-" + ConvertUInt32ToString((UInt32)i);
250 AddString(s, s2);
251 }
252 return s;
253}
254
255STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
256{
257 COM_TRY_BEGIN
258 NWindows::NCOM::CPropVariant prop;
259 switch(propID)
260 {
261 case kpidNumBlocks: if (!_useSeq) prop = _numBlocks; break;
262 case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
263 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
264 }
265 prop.Detach(value);
266 return S_OK;
267 COM_TRY_END
268}
269
270STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
271{
272 *numItems = 1;
273 return S_OK;
274}
275
276STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value)
277{
278 COM_TRY_BEGIN
279 NWindows::NCOM::CPropVariant prop;
280 switch(propID)
281 {
282 case kpidSize: if (_unpackSizeDefined) prop = _unpackSize; break;
283 case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
284 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
285 }
286 prop.Detach(value);
287 return S_OK;
288 COM_TRY_END
289}
290
291
292struct COpenCallbackWrap
293{
294 ICompressProgress p;
295 IArchiveOpenCallback *OpenCallback;
296 HRESULT Res;
297 COpenCallbackWrap(IArchiveOpenCallback *progress);
298};
299
300static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */)
301{
302 COpenCallbackWrap *p = (COpenCallbackWrap *)pp;
303 p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
304 return (SRes)p->Res;
305}
306
307COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback)
308{
309 p.Progress = OpenCallbackProgress;
310 OpenCallback = callback;
311 Res = SZ_OK;
312}
313
314struct CXzsCPP
315{
316 CXzs p;
317 CXzsCPP() { Xzs_Construct(&p); }
318 ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
319};
320
321HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback)
322{
323 CSeekInStreamWrap inStreamImp(inStream);
324
325 CLookToRead lookStream;
326 LookToRead_CreateVTable(&lookStream, True);
327 lookStream.realStream = &inStreamImp.p;
328 LookToRead_Init(&lookStream);
329
330 COpenCallbackWrap openWrap(callback);
331 RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize));
332 RINOK(callback->SetTotal(NULL, &_packSize));
333
334 CXzsCPP xzs;
335 SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &_startPosition, &openWrap.p, &g_Alloc);
336 if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
337 res = SZ_OK;
338 if (res == SZ_OK)
339 {
340 _packSize -= _startPosition;
341 _unpackSize = Xzs_GetUnpackSize(&xzs.p);
342 _unpackSizeDefined = _packSizeDefined = true;
343 _numBlocks = (UInt64)Xzs_GetNumBlocks(&xzs.p);
344
345 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
346 CXzStreamFlags st;
347 CSeqInStreamWrap inStreamWrap(inStream);
348 SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
349
350 if (res2 == SZ_OK)
351 {
352 CXzBlock block;
353 Bool isIndex;
354 UInt32 headerSizeRes;
355 res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes);
356 if (res2 == SZ_OK && !isIndex)
357 {
358 int numFilters = XzBlock_GetNumFilters(&block);
359 for (int i = 0; i < numFilters; i++)
360 AddString(_methodsString, GetMethodString(block.filters[i]));
361 }
362 }
363 AddString(_methodsString, GetCheckString(xzs.p));
364 }
365
366 if (res != SZ_OK || _startPosition != 0)
367 {
368 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
369 CXzStreamFlags st;
370 CSeqInStreamWrap inStreamWrap(inStream);
371 SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
372 if (res2 == SZ_OK)
373 {
374 res = res2;
375 _startPosition = 0;
376 _useSeq = True;
377 _unpackSizeDefined = _packSizeDefined = false;
378 }
379 }
380 if (res == SZ_ERROR_NO_ARCHIVE)
381 return S_FALSE;
382 RINOK(SResToHRESULT(res));
383 _stream = inStream;
384 _seqStream = inStream;
385 return S_OK;
386}
387
388STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
389{
390 COM_TRY_BEGIN
391 try
392 {
393 Close();
394 return Open2(inStream, callback);
395 }
396 catch(...) { return S_FALSE; }
397 COM_TRY_END
398}
399
400STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
401{
402 Close();
403 _seqStream = stream;
404 return S_OK;
405}
406
407STDMETHODIMP CHandler::Close()
408{
409 _numBlocks = 0;
410 _useSeq = true;
411 _unpackSizeDefined = _packSizeDefined = false;
412 _methodsString.Empty();
413 _stream.Release();
414 _seqStream.Release();
415 return S_OK;
416}
417
418class CSeekToSeqStream:
419 public IInStream,
420 public CMyUnknownImp
421{
422public:
423 CMyComPtr<ISequentialInStream> Stream;
424 MY_UNKNOWN_IMP1(IInStream)
425
426 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
427 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
428};
429
430STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
431{
432 return Stream->Read(data, size, processedSize);
433}
434
435STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
436
437struct CXzUnpackerCPP
438{
439 Byte *InBuf;
440 Byte *OutBuf;
441 CXzUnpacker p;
442 CXzUnpackerCPP(): InBuf(0), OutBuf(0) {}
443 ~CXzUnpackerCPP()
444 {
445 XzUnpacker_Free(&p);
446 MyFree(InBuf);
447 MyFree(OutBuf);
448 }
449};
450
451STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
452 Int32 testMode, IArchiveExtractCallback *extractCallback)
453{
454 COM_TRY_BEGIN
455 if (numItems == 0)
456 return S_OK;
457 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
458 return E_INVALIDARG;
459
460 extractCallback->SetTotal(_packSize);
461 UInt64 currentTotalPacked = 0;
462 RINOK(extractCallback->SetCompleted(&currentTotalPacked));
463 CMyComPtr<ISequentialOutStream> realOutStream;
464 Int32 askMode = testMode ?
465 NExtract::NAskMode::kTest :
466 NExtract::NAskMode::kExtract;
467
468 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
469
470 if (!testMode && !realOutStream)
471 return S_OK;
472
473 extractCallback->PrepareOperation(askMode);
474
475 if (_stream)
476 {
477 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
478 }
479
480 CLocalProgress *lps = new CLocalProgress;
481 CMyComPtr<ICompressProgressInfo> progress = lps;
482 lps->Init(extractCallback, true);
483
484 CCompressProgressWrap progressWrap(progress);
485
486 SRes res;
487
488 const UInt32 kInBufSize = 1 << 15;
489 const UInt32 kOutBufSize = 1 << 21;
490
491 UInt32 inPos = 0;
492 UInt32 inSize = 0;
493 UInt32 outPos = 0;
494 CXzUnpackerCPP xzu;
495 res = XzUnpacker_Create(&xzu.p, &g_Alloc);
496 if (res == SZ_OK)
497 {
498 xzu.InBuf = (Byte *)MyAlloc(kInBufSize);
499 xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize);
500 if (xzu.InBuf == 0 || xzu.OutBuf == 0)
501 res = SZ_ERROR_MEM;
502 }
503 if (res == SZ_OK)
504 for (;;)
505 {
506 if (inPos == inSize)
507 {
508 inPos = inSize = 0;
509 RINOK(_seqStream->Read(xzu.InBuf, kInBufSize, &inSize));
510 }
511
512 SizeT inLen = inSize - inPos;
513 SizeT outLen = kOutBufSize - outPos;
514 ECoderStatus status;
515 res = XzUnpacker_Code(&xzu.p,
516 xzu.OutBuf + outPos, &outLen,
517 xzu.InBuf + inPos, &inLen,
518 (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status);
519
520 // printf("\n_inPos = %6d inLen = %5d, outLen = %5d", inPos, inLen, outLen);
521
522 inPos += (UInt32)inLen;
523 outPos += (UInt32)outLen;
524 lps->InSize += inLen;
525 lps->OutSize += outLen;
526
527 bool finished = (((inLen == 0) && (outLen == 0)) || res != SZ_OK);
528
529 if (outPos == kOutBufSize || finished)
530 {
531 if (realOutStream && outPos > 0)
532 {
533 RINOK(WriteStream(realOutStream, xzu.OutBuf, outPos));
534 }
535 outPos = 0;
536 }
537 if (finished)
538 {
539 _packSize = lps->InSize;
540 _unpackSize = lps->OutSize;
541 _packSizeDefined = _unpackSizeDefined = true;
542 if (res == SZ_OK)
543 {
544 if (status == CODER_STATUS_NEEDS_MORE_INPUT)
545 {
546 if (XzUnpacker_IsStreamWasFinished(&xzu.p))
547 _packSize -= xzu.p.padSize;
548 else
549 res = SZ_ERROR_DATA;
550 }
551 else
552 res = SZ_ERROR_DATA;
553 }
554 break;
555 }
556 RINOK(lps->SetCur());
557 }
558
559 Int32 opRes;
560 switch(res)
561 {
562 case SZ_OK:
563 opRes = NExtract::NOperationResult::kOK; break;
564 case SZ_ERROR_UNSUPPORTED:
565 opRes = NExtract::NOperationResult::kUnSupportedMethod; break;
566 case SZ_ERROR_CRC:
567 opRes = NExtract::NOperationResult::kCRCError; break;
568 case SZ_ERROR_DATA:
569 case SZ_ERROR_ARCHIVE:
570 case SZ_ERROR_NO_ARCHIVE:
571 opRes = NExtract::NOperationResult::kDataError; break;
572 default:
573 return SResToHRESULT(res);
574 }
575 realOutStream.Release();
576 RINOK(extractCallback->SetOperationResult(opRes));
577 return S_OK;
578 COM_TRY_END
579}
580
581#ifndef EXTRACT_ONLY
582
583STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
584{
585 *timeType = NFileTimeType::kUnix;
586 return S_OK;
587}
588
589STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
590 IArchiveUpdateCallback *updateCallback)
591{
592 CSeqOutStreamWrap seqOutStream(outStream);
593
594 if (numItems == 0)
595 {
596 SRes res = Xz_EncodeEmpty(&seqOutStream.p);
597 return SResToHRESULT(res);
598 }
599
600 if (numItems != 1)
601 return E_INVALIDARG;
602
603 Int32 newData, newProps;
604 UInt32 indexInArchive;
605 if (!updateCallback)
606 return E_FAIL;
607 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
608
609 if (IntToBool(newProps))
610 {
611 {
612 NCOM::CPropVariant prop;
613 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
614 if (prop.vt != VT_EMPTY)
615 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
616 return E_INVALIDARG;
617 }
618 }
619
620 if (IntToBool(newData))
621 {
622 {
623 UInt64 size;
624 NCOM::CPropVariant prop;
625 RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
626 if (prop.vt != VT_UI8)
627 return E_INVALIDARG;
628 size = prop.uhVal.QuadPart;
629 RINOK(updateCallback->SetTotal(size));
630 }
631
632 CLzma2EncProps lzma2Props;
633 Lzma2EncProps_Init(&lzma2Props);
634
635 lzma2Props.lzmaProps.level = _level;
636
637 CMyComPtr<ISequentialInStream> fileInStream;
638 RINOK(updateCallback->GetStream(0, &fileInStream));
639
640 CSeqInStreamWrap seqInStream(fileInStream);
641
642 for (int i = 0; i < _methods.Size(); i++)
643 {
644 COneMethodInfo &m = _methods[i];
645 SetCompressionMethod2(m
646 #ifndef _7ZIP_ST
647 , _numThreads
648 #endif
649 );
650 if (m.IsLzma())
651 {
652 for (int j = 0; j < m.Props.Size(); j++)
653 {
654 const CProp &prop = m.Props[j];
655 RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props));
656 }
657 }
658 }
659
660 #ifndef _7ZIP_ST
661 lzma2Props.numTotalThreads = _numThreads;
662 #endif
663
664 CLocalProgress *lps = new CLocalProgress;
665 CMyComPtr<ICompressProgressInfo> progress = lps;
666 lps->Init(updateCallback, true);
667
668 CCompressProgressWrap progressWrap(progress);
669 SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &lzma2Props, False, &progressWrap.p);
670 if (res == SZ_OK)
671 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
672 return SResToHRESULT(res);
673 }
674 if (indexInArchive != 0)
675 return E_INVALIDARG;
676 if (_stream)
677 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
678 return NCompress::CopyStream(_stream, outStream, 0);
679}
680
681STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
682{
683 COM_TRY_BEGIN
684 BeforeSetProperty();
685 for (int i = 0; i < numProps; i++)
686 {
687 RINOK(SetProperty(names[i], values[i]));
688 }
689 return S_OK;
690 COM_TRY_END
691}
692
693#endif
694
695static IInArchive *CreateArc() { return new NArchive::NXz::CHandler; }
696#ifndef EXTRACT_ONLY
697static IOutArchive *CreateArcOut() { return new NArchive::NXz::CHandler; }
698#else
699#define CreateArcOut 0
700#endif
701
702static CArcInfo g_ArcInfo =
703 { L"xz", L"xz txz", L"* .tar", 0xC, {0xFD, '7' , 'z', 'X', 'Z', '\0'}, 6, true, CreateArc, CreateArcOut };
704
705REGISTER_ARC(xz)
706
707}}