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