1 // XzDecoder.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 
7 #include "../Common/StreamUtils.h"
8 
9 #include "../Archive/IArchive.h"
10 
11 #include "XzDecoder.h"
12 
13 using namespace NArchive;
14 
15 namespace NCompress {
16 namespace NXz {
17 
18 
CXzUnpackerCPP()19 CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(NULL), OutBuf(NULL)
20 {
21   XzUnpacker_Construct(&p, &g_Alloc);
22 }
23 
~CXzUnpackerCPP()24 CXzUnpackerCPP::~CXzUnpackerCPP()
25 {
26   XzUnpacker_Free(&p);
27   MidFree(InBuf);
28   MidFree(OutBuf);
29 }
30 
31 
Clear()32 void CStatInfo::Clear()
33 {
34   InSize = 0;
35   OutSize = 0;
36   PhySize = 0;
37 
38   NumStreams = 0;
39   NumBlocks = 0;
40 
41   UnpackSize_Defined = false;
42 
43   NumStreams_Defined = false;
44   NumBlocks_Defined = false;
45 
46   IsArc = false;
47   UnexpectedEnd = false;
48   DataAfterEnd = false;
49   Unsupported = false;
50   HeadersError = false;
51   DataError = false;
52   CrcError = false;
53 }
54 
55 
Decode(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,const UInt64 * outSizeLimit,bool finishStream,ICompressProgressInfo * progress)56 HRESULT CDecoder::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream,
57     const UInt64 *outSizeLimit, bool finishStream, ICompressProgressInfo *progress)
58 {
59   const size_t kInBufSize = (size_t)1 << 20;
60   const size_t kOutBufSize = (size_t)1 << 21;
61 
62   Clear();
63   DecodeRes = SZ_OK;
64 
65   XzUnpacker_Init(&xzu.p);
66 
67   if (!xzu.InBuf)
68   {
69     xzu.InBuf = (Byte *)MidAlloc(kInBufSize);
70     if (!xzu.InBuf)
71       return E_OUTOFMEMORY;
72   }
73   if (!xzu.OutBuf)
74   {
75     xzu.OutBuf = (Byte *)MidAlloc(kOutBufSize);
76     if (!xzu.OutBuf)
77       return E_OUTOFMEMORY;
78   }
79 
80   UInt32 inSize = 0;
81   UInt32 inPos = 0;
82   SizeT outPos = 0;
83 
84   HRESULT readRes = S_OK;
85 
86   for (;;)
87   {
88     if (inPos == inSize && readRes == S_OK)
89     {
90       inPos = inSize = 0;
91       readRes = seqInStream->Read(xzu.InBuf, kInBufSize, &inSize);
92     }
93 
94     SizeT inLen = inSize - inPos;
95     SizeT outLen = kOutBufSize - outPos;
96     ECoderFinishMode finishMode = CODER_FINISH_ANY;
97 
98     /*
99     // 17.01 : the code was disabled:
100     if (inSize == 0)
101       finishMode = CODER_FINISH_END;
102     */
103 
104     if (outSizeLimit)
105     {
106       const UInt64 rem = *outSizeLimit - OutSize;
107       if (outLen >= rem)
108       {
109         outLen = (SizeT)rem;
110         if (finishStream)
111           finishMode = CODER_FINISH_END;
112       }
113     }
114 
115     ECoderStatus status;
116 
117     const SizeT outLenRequested = outLen;
118 
119     SRes res = XzUnpacker_Code(&xzu.p,
120         xzu.OutBuf + outPos, &outLen,
121         xzu.InBuf + inPos, &inLen,
122         finishMode, &status);
123 
124     DecodeRes = res;
125 
126     inPos += (UInt32)inLen;
127     outPos += outLen;
128 
129     InSize += inLen;
130     OutSize += outLen;
131 
132     bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK);
133 
134     if (outLen >= outLenRequested || finished)
135     {
136       if (outStream && outPos != 0)
137       {
138         RINOK(WriteStream(outStream, xzu.OutBuf, outPos));
139       }
140       outPos = 0;
141     }
142 
143     if (progress)
144     {
145       RINOK(progress->SetRatioInfo(&InSize, &OutSize));
146     }
147 
148     if (!finished)
149       continue;
150 
151     {
152       PhySize = InSize;
153       NumStreams = xzu.p.numStartedStreams;
154       if (NumStreams > 0)
155         IsArc = true;
156       NumBlocks = xzu.p.numTotalBlocks;
157 
158       UnpackSize_Defined = true;
159       NumStreams_Defined = true;
160       NumBlocks_Defined = true;
161 
162       UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p);
163 
164       if (res == SZ_OK)
165       {
166         if (status == CODER_STATUS_NEEDS_MORE_INPUT)
167         {
168           extraSize = 0;
169           if (!XzUnpacker_IsStreamWasFinished(&xzu.p))
170           {
171             // finished at padding bytes, but padding is not aligned for 4
172             UnexpectedEnd = true;
173             res = SZ_ERROR_DATA;
174           }
175         }
176         else // status == CODER_STATUS_NOT_FINISHED
177           res = SZ_ERROR_DATA;
178       }
179       else if (res == SZ_ERROR_NO_ARCHIVE)
180       {
181         if (InSize == extraSize)
182           IsArc = false;
183         else
184         {
185           if (extraSize != 0 || inPos != inSize)
186           {
187             DataAfterEnd = true;
188             res = SZ_OK;
189           }
190         }
191       }
192 
193       DecodeRes = res;
194       PhySize -= extraSize;
195 
196       switch (res)
197       {
198         case SZ_OK: break;
199         case SZ_ERROR_NO_ARCHIVE: IsArc = false; break;
200         case SZ_ERROR_ARCHIVE: HeadersError = true; break;
201         case SZ_ERROR_UNSUPPORTED: Unsupported = true; break;
202         case SZ_ERROR_CRC: CrcError = true; break;
203         case SZ_ERROR_DATA: DataError = true; break;
204         default: DataError = true; break;
205       }
206 
207       return readRes;
208     }
209   }
210 }
211 
212 
Get_Extract_OperationResult() const213 Int32 CDecoder::Get_Extract_OperationResult() const
214 {
215   Int32 opRes;
216   if (!IsArc)
217     opRes = NExtract::NOperationResult::kIsNotArc;
218   else if (UnexpectedEnd)
219     opRes = NExtract::NOperationResult::kUnexpectedEnd;
220   else if (DataAfterEnd)
221     opRes = NExtract::NOperationResult::kDataAfterEnd;
222   else if (CrcError)
223     opRes = NExtract::NOperationResult::kCRCError;
224   else if (Unsupported)
225     opRes = NExtract::NOperationResult::kUnsupportedMethod;
226   else if (HeadersError)
227     opRes = NExtract::NOperationResult::kDataError;
228   else if (DataError)
229     opRes = NExtract::NOperationResult::kDataError;
230   else if (DecodeRes != SZ_OK)
231     opRes = NExtract::NOperationResult::kDataError;
232   else
233     opRes = NExtract::NOperationResult::kOK;
234   return opRes;
235 }
236 
237 
238 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)239 HRESULT CComDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
240     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
241 {
242   RINOK(_decoder.Decode(inStream, outStream, outSize, _finishStream, progress));
243   Int32 opRes = _decoder.Get_Extract_OperationResult();
244   if (opRes == NArchive::NExtract::NOperationResult::kUnsupportedMethod)
245     return E_NOTIMPL;
246   if (opRes != NArchive::NExtract::NOperationResult::kOK)
247     return S_FALSE;
248   return S_OK;
249 }
250 
SetFinishMode(UInt32 finishMode)251 STDMETHODIMP CComDecoder::SetFinishMode(UInt32 finishMode)
252 {
253   _finishStream = (finishMode != 0);
254   return S_OK;
255 }
256 
GetInStreamProcessedSize(UInt64 * value)257 STDMETHODIMP CComDecoder::GetInStreamProcessedSize(UInt64 *value)
258 {
259   *value = _decoder.InSize;
260   return S_OK;
261 }
262 
263 }}
264