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