xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/MslzHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // MslzHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/MyString.h"
9 
10 #include "../../Windows/PropVariant.h"
11 
12 #include "../Common/InBuffer.h"
13 #include "../Common/ProgressUtils.h"
14 #include "../Common/RegisterArc.h"
15 #include "../Common/StreamUtils.h"
16 
17 #include "Common/DummyOutStream.h"
18 
19 namespace NArchive {
20 namespace NMslz {
21 
22 static const UInt32 kUnpackSizeMax = 0xFFFFFFE0;
23 
24 Z7_CLASS_IMP_CHandler_IInArchive_1(
25   IArchiveOpenSeq
26 )
27   CMyComPtr<IInStream> _inStream;
28   CMyComPtr<ISequentialInStream> _seqStream;
29 
30   bool _isArc;
31   bool _needSeekToStart;
32   bool _dataAfterEnd;
33   bool _needMoreInput;
34 
35   bool _packSize_Defined;
36   bool _unpackSize_Defined;
37 
38   UInt32 _unpackSize;
39   UInt64 _packSize;
40   UInt64 _originalFileSize;
41   UString _name;
42 
43   void ParseName(Byte replaceByte, IArchiveOpenCallback *callback);
44 };
45 
46 static const Byte kProps[] =
47 {
48   kpidPath,
49   kpidSize,
50   kpidPackSize,
51 };
52 
53 IMP_IInArchive_Props
54 IMP_IInArchive_ArcProps_NO_Table
55 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))56 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
57 {
58   *numItems = 1;
59   return S_OK;
60 }
61 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))62 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
63 {
64   COM_TRY_BEGIN
65   NWindows::NCOM::CPropVariant prop;
66   switch (propID)
67   {
68     case kpidExtension: prop = "mslz"; break;
69     case kpidIsNotArcType: prop = true; break;
70     case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
71     case kpidErrorFlags:
72     {
73       UInt32 v = 0;
74       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
75       if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
76       if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
77       prop = v;
78       break;
79     }
80   }
81   prop.Detach(value);
82   return S_OK;
83   COM_TRY_END
84 }
85 
86 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32,PROPID propID,PROPVARIANT * value))87 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
88 {
89   COM_TRY_BEGIN
90   NWindows::NCOM::CPropVariant prop;
91   switch (propID)
92   {
93     case kpidPath: if (!_name.IsEmpty()) prop = _name; break;
94     case kpidSize: if (_unpackSize_Defined || _inStream) prop = _unpackSize; break;
95     case kpidPackSize: if (_packSize_Defined || _inStream) prop = _packSize; break;
96   }
97   prop.Detach(value);
98   return S_OK;
99   COM_TRY_END
100 }
101 
102 static const unsigned kSignatureSize = 9;
103 static const unsigned kHeaderSize = kSignatureSize + 1 + 4;
104 #define MSLZ_SIGNATURE { 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33, 0x41 }
105 // old signature: 53 5A 20 88 F0 27 33
106 static const Byte kSignature[kSignatureSize] = MSLZ_SIGNATURE;
107 
108 // we support only 3 chars strings here
109 static const char * const g_Exts[] =
110 {
111     "bin"
112   , "dll"
113   , "exe"
114   , "kmd"
115   , "pdf"
116   , "sys"
117 };
118 
ParseName(Byte replaceByte,IArchiveOpenCallback * callback)119 void CHandler::ParseName(Byte replaceByte, IArchiveOpenCallback *callback)
120 {
121   if (!callback)
122     return;
123   Z7_DECL_CMyComPtr_QI_FROM(IArchiveOpenVolumeCallback, volumeCallback, callback)
124   if (!volumeCallback)
125     return;
126 
127   NWindows::NCOM::CPropVariant prop;
128   if (volumeCallback->GetProperty(kpidName, &prop) != S_OK || prop.vt != VT_BSTR)
129     return;
130 
131   UString s = prop.bstrVal;
132   if (s.IsEmpty() ||
133       s.Back() != L'_')
134     return;
135 
136   s.DeleteBack();
137   _name = s;
138 
139   if (replaceByte == 0)
140   {
141     if (s.Len() < 3 || s[s.Len() - 3] != '.')
142       return;
143     for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Exts); i++)
144     {
145       const char *ext = g_Exts[i];
146       if (s[s.Len() - 2] == (Byte)ext[0] &&
147           s[s.Len() - 1] == (Byte)ext[1])
148       {
149         replaceByte = (Byte)ext[2];
150         break;
151       }
152     }
153   }
154 
155   if (replaceByte >= 0x20 && replaceByte < 0x80)
156     _name.Add_Char((char)replaceByte);
157 }
158 
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback))159 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */,
160     IArchiveOpenCallback *callback))
161 {
162   COM_TRY_BEGIN
163   {
164     Close();
165     _needSeekToStart = true;
166     Byte buffer[kHeaderSize];
167     RINOK(ReadStream_FALSE(stream, buffer, kHeaderSize))
168     if (memcmp(buffer, kSignature, kSignatureSize) != 0)
169       return S_FALSE;
170     _unpackSize = GetUi32(buffer + 10);
171     if (_unpackSize > kUnpackSizeMax)
172       return S_FALSE;
173     RINOK(InStream_GetSize_SeekToEnd(stream, _originalFileSize))
174     _packSize = _originalFileSize;
175 
176     ParseName(buffer[kSignatureSize], callback);
177 
178     _isArc = true;
179     _unpackSize_Defined = true;
180     _inStream = stream;
181     _seqStream = stream;
182   }
183   return S_OK;
184   COM_TRY_END
185 }
186 
Z7_COM7F_IMF(CHandler::Close ())187 Z7_COM7F_IMF(CHandler::Close())
188 {
189   _originalFileSize = 0;
190   _packSize = 0;
191   _unpackSize = 0;
192 
193   _isArc = false;
194   _needSeekToStart = false;
195   _dataAfterEnd = false;
196   _needMoreInput = false;
197 
198   _packSize_Defined = false;
199   _unpackSize_Defined =  false;
200 
201   _seqStream.Release();
202   _inStream.Release();
203   _name.Empty();
204   return S_OK;
205 }
206 
207 // MslzDec is modified LZSS algorithm of Haruhiko Okumura:
208 //   maxLen = 18; Okumura
209 //   maxLen = 16; MS
210 
211 #define PROGRESS_AND_WRITE \
212   if ((dest & kMask) == 0) { if (outStream) RINOK(WriteStream(outStream, buf, kBufSize)); \
213     if ((dest & ((1 << 20) - 1)) == 0) \
214     if (progress) \
215       { UInt64 inSize = inStream.GetProcessedSize(); UInt64 outSize = dest; \
216         RINOK(progress->SetRatioInfo(&inSize, &outSize)); }}
217 
MslzDec(CInBuffer & inStream,ISequentialOutStream * outStream,UInt32 unpackSize,bool & needMoreData,ICompressProgressInfo * progress)218 static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, bool &needMoreData, ICompressProgressInfo *progress)
219 {
220   const unsigned kBufSize = (1 << 12);
221   const unsigned kMask = kBufSize - 1;
222   Byte buf[kBufSize];
223   UInt32 dest = 0;
224   memset(buf, ' ', kBufSize);
225 
226   while (dest < unpackSize)
227   {
228     Byte b;
229     if (!inStream.ReadByte(b))
230     {
231       needMoreData = true;
232       return S_FALSE;
233     }
234 
235     for (unsigned mask = (unsigned)b | 0x100; mask > 1 && dest < unpackSize; mask >>= 1)
236     {
237       if (!inStream.ReadByte(b))
238       {
239         needMoreData = true;
240         return S_FALSE;
241       }
242 
243       if (mask & 1)
244       {
245         buf[dest++ & kMask] = b;
246         PROGRESS_AND_WRITE
247       }
248       else
249       {
250         Byte b1;
251         if (!inStream.ReadByte(b1))
252         {
253           needMoreData = true;
254           return S_FALSE;
255         }
256         const unsigned kMaxLen = 16; // 18 in Okumura's code.
257         unsigned src = (((((unsigned)b1 & 0xF0) << 4) | b) + kMaxLen) & kMask;
258         unsigned len = (b1 & 0xF) + 3;
259         if (len > kMaxLen || dest + len > unpackSize)
260           return S_FALSE;
261 
262         do
263         {
264           buf[dest++ & kMask] = buf[src++ & kMask];
265           PROGRESS_AND_WRITE
266         }
267         while (--len != 0);
268       }
269     }
270   }
271 
272   if (outStream)
273     RINOK(WriteStream(outStream, buf, dest & kMask))
274   return S_OK;
275 }
276 
Z7_COM7F_IMF(CHandler::OpenSeq (ISequentialInStream * stream))277 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
278 {
279   COM_TRY_BEGIN
280   Close();
281   _isArc = true;
282   _seqStream = stream;
283   return S_OK;
284   COM_TRY_END
285 }
286 
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))287 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
288     Int32 testMode, IArchiveExtractCallback *extractCallback))
289 {
290   COM_TRY_BEGIN
291   if (numItems == 0)
292     return S_OK;
293   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
294     return E_INVALIDARG;
295 
296   // RINOK(extractCallback->SetTotal(_unpackSize))
297   Int32 opRes;
298   {
299   CMyComPtr<ISequentialOutStream> realOutStream;
300   const Int32 askMode = testMode ?
301       NExtract::NAskMode::kTest :
302       NExtract::NAskMode::kExtract;
303   RINOK(extractCallback->GetStream(0, &realOutStream, askMode))
304   if (!testMode && !realOutStream)
305     return S_OK;
306 
307   RINOK(extractCallback->PrepareOperation(askMode))
308 
309   CMyComPtr2_Create<ISequentialOutStream, CDummyOutStream> outStream;
310   outStream->SetStream(realOutStream);
311   outStream->Init();
312   // realOutStream.Release();
313 
314   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
315   lps->Init(extractCallback, false);
316 
317   if (_needSeekToStart)
318   {
319     if (!_inStream)
320       return E_FAIL;
321     RINOK(InStream_SeekToBegin(_inStream))
322   }
323   else
324     _needSeekToStart = true;
325 
326   opRes = NExtract::NOperationResult::kDataError;
327 
328   bool isArc = false;
329   bool needMoreInput = false;
330   try
331   {
332     CInBuffer s;
333     if (!s.Create(1 << 20))
334       return E_OUTOFMEMORY;
335     s.SetStream(_seqStream);
336     s.Init();
337 
338     Byte buffer[kHeaderSize];
339     if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize)
340     {
341       UInt32 unpackSize;
342       if (memcmp(buffer, kSignature, kSignatureSize) == 0)
343       {
344         unpackSize = GetUi32(buffer + 10);
345         if (unpackSize <= kUnpackSizeMax)
346         {
347           const HRESULT result = MslzDec(s, outStream, unpackSize, needMoreInput, lps);
348           if (result == S_OK)
349             opRes = NExtract::NOperationResult::kOK;
350           else if (result != S_FALSE)
351             return result;
352           _unpackSize = unpackSize;
353           _unpackSize_Defined = true;
354 
355           _packSize = s.GetProcessedSize();
356           _packSize_Defined = true;
357 
358           if (_inStream && _packSize < _originalFileSize)
359             _dataAfterEnd = true;
360 
361           isArc = true;
362         }
363       }
364     }
365   }
366   catch (CInBufferException &e) { return e.ErrorCode; }
367 
368   _isArc = isArc;
369   if (isArc)
370     _needMoreInput = needMoreInput;
371   if (!_isArc)
372     opRes = NExtract::NOperationResult::kIsNotArc;
373   else if (_needMoreInput)
374     opRes = NExtract::NOperationResult::kUnexpectedEnd;
375   else if (_dataAfterEnd)
376     opRes = NExtract::NOperationResult::kDataAfterEnd;
377   }
378   return extractCallback->SetOperationResult(opRes);
379   COM_TRY_END
380 }
381 
382 REGISTER_ARC_I(
383   "MsLZ", "mslz", NULL, 0xD5,
384   kSignature,
385   0,
386   0,
387   NULL)
388 
389 }}
390